Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Bug report
description: Something in the SDK, docs, or examples doesn't work as documented.
labels: [bug]
body:
- type: textarea
id: what-happened
attributes:
label: What happened?
description: What you expected vs. what actually happened.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Minimal reproduction
description: >-
A minimal code snippet. Do **not** include real `apiSid` / `apiToken` values or
real printer / device UIDs — use placeholders.
render: ts
validations:
required: true
- type: input
id: sdk-version
attributes:
label: expedy-sdk-node version
placeholder: "1.1.0"
validations:
required: true
- type: input
id: node-version
attributes:
label: Node.js version
placeholder: "node --version"
validations:
required: true
- type: dropdown
id: resource
attributes:
label: Affected resource
options:
- printers (cloud thermal printer)
- devices (Raspberry Pi / USB)
- Documentation only
- Not sure
validations:
required: true
25 changes: 25 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Feature request
description: Propose a new SDK method, type, doc page, or example.
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: What's missing?
description: What are you trying to do that the SDK / docs don't currently support?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: A method signature, a new doc page, an example — whatever fits.
validations:
required: false
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Any workaround you're currently using.
validations:
required: false
15 changes: 15 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Summary

<!-- What does this change do, and why? -->

## Checklist

- [ ] `npm run typecheck && npm run build && npm run typecheck:examples` pass locally
- [ ] `npm test` passes locally
- [ ] If a request/response field changed: `src/types/*.ts`, `openapi.yaml`, and the
matching `docs/api/**/*.md` page were all updated together
- [ ] No real credentials, UIDs, or internal URLs were introduced (this is a public repo)

## Test plan

<!-- How did you verify this change? -->
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run typecheck
- run: npm run build
- run: npm run typecheck:examples
- run: node --test test/*.test.mjs
72 changes: 72 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# AGENTS.md

Guidance for coding agents (Claude Code, Cursor, GitHub Copilot, …) working in or against
this repository, and a condensed reference for agents integrating `expedy-sdk-node` into
someone else's codebase.

## What this is

Official Node.js SDK + API documentation for the **Expedy Print API v2**. It sends print
jobs to two kinds of hardware:

| Resource | Hardware | Print method |
| --- | --- | --- |
| `printers` | Expedy cloud thermal receipt printer (own internet connection) | `client.printers.createPrintJob(printerUid, { printer_msg, ... })` |
| `devices` | Raspberry Pi gateway + a third-party USB printer plugged into it | `client.devices.usb.createPrintJob(deviceUid, usbPort, { usb_msg, ... })` |

Read [`docs/concepts/printers-vs-devices.md`](docs/concepts/printers-vs-devices.md) before
writing code against either endpoint — picking the wrong one is the most common mistake.

`displays` and `medias` are **out of scope** for this repository.

## Non-obvious things to get right

- **Authentication is not Bearer.** The `Authorization` header is the raw
`<API_SID>:<API_TOKEN>` value, colon-separated, **no prefix**. `ExpedyClient` builds this
automatically — never hand-construct the header.
- **`printer_han` for Chinese/Japanese/Korean.** Without this field, CJK characters are
silently replaced with `?` **before the job reaches the printer** — no error is raised.
If a user asks to print non-Latin text and the code doesn't set `printer_han`, that's a
bug. See [`docs/receipt-layout/asian-characters.md`](docs/receipt-layout/asian-characters.md).
Values: `"cn"` Chinese, `"kr"` Korean, `"jp"` Japanese. Omit for Latin scripts.
- **`200` means accepted, not printed.** Both print endpoints are asynchronous. Don't tell a
user "your ticket printed" based on the SDK call resolving — see
[`docs/concepts/delivery-and-idempotency.md`](docs/concepts/delivery-and-idempotency.md).
- **No de-duplication.** Retrying a print request after a network error can produce two
physical tickets. Track `request_uid` if you add retry logic.
- **`printer_msg` / `usb_msg` carries an XML-like tag language**, not HTML — `<C>`, `<BOLD>`,
`<IMG>`, `<QR>`, `<CUT/>`, `<PULSE/>`, plus one-shot provisioning tags
(`<SETWIFI>`, `<SETSNTP>`, `<SETAPN>`, `<SETKEEPALIVE>`, `<UNSETBEEP/>`). Full reference:
[`docs/receipt-layout/text-layout-tags.md`](docs/receipt-layout/text-layout-tags.md).
- **`printer_status` is an activation flag**, not connectivity. `"0"` means suspended by
ExpedyPRINT (usually billing), not "printer is offline".
- Errors are `ExpedyError` (network/config) or `ExpedyApiError` (`status`, `rawBody`,
`requestUid`), both exported from the package root. Always read `err.message` /
`rawBody.message` rather than branching on `status` alone.

## Where to look

- **Full API reference**: [`docs/README.md`](docs/README.md) — reading order included.
- **Machine-readable spec**: [`openapi.yaml`](openapi.yaml) — all 16 operations, request/
response schemas, `printer_han` enum.
- **Runnable examples**: [`examples/`](examples/) — one file per feature, each a complete
standalone script (`node --experimental-strip-types examples/<name>.ts`).
- **SDK source**: `src/client.ts` (HTTP layer, ~130 lines), `src/resources/*.ts` (one
method per endpoint), `src/types/*.ts` (request/response shapes with JSDoc).
- **Canonical docs site**: <https://docs.expedy.io/> — same content as `docs/`, plus
hardware setup guides and ~190 integration guides out of this repo's scope (see
[`docs/integrations.md`](docs/integrations.md) for the index).

## Working on this repository

- `npm run typecheck` — type-check `src/` only.
- `npm run build` — compile to `dist/`.
- `npm run typecheck:examples` — type-check `examples/` against the compiled types.
- `npm test` — build, then run the test suite (`node --test`, no test framework
dependency).
- Touching a field in `src/types/*.ts`? Update the matching schema in `openapi.yaml` and the
matching page under `docs/api/` in the same change — see
[`CONTRIBUTING.md`](CONTRIBUTING.md).
- This is a **public** repository. Never commit real credentials, UIDs, or internal URLs —
use the placeholder values already used throughout `docs/` and `examples/`
(`WP0RGS1SEDZ`, `MMAAZ112PI`, `example.com`, …).
56 changes: 56 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Changelog

All notable changes to this project are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
[Semantic Versioning](https://semver.org/).

## [1.1.0]

### Added

- `printer_han` field on `CreatePrintJobRequest` and `CreateUsbPrintJobRequest` — required
to print Chinese, Japanese or Korean text. Without it, CJK characters are silently
replaced with `?` before the job reaches the printer. New `PrinterHan` /
`PrinterHanScript` exported types, new
[`docs/receipt-layout/asian-characters.md`](docs/receipt-layout/asian-characters.md)
reference page, and two new runnable examples
(`examples/receipt-asian-characters.ts`, `examples/device-rpi-usb-print-asian.ts`).
- `docs/getting-started/errors.md` — SDK error types, status codes by endpoint, retry
guidance.
- `docs/concepts/delivery-and-idempotency.md` — what a `200` response actually guarantees,
and how to avoid double prints.
- `docs/integrations.md` — index of no-code / e-commerce / delivery platforms that connect
to Expedy PRINT.
- `openapi.yaml` — OpenAPI 3.1 description of all 16 API operations.
- `AGENTS.md` and `llms.txt` for coding agents and LLM-based tools.
- `CONTRIBUTING.md` and `SECURITY.md`.
- Test suite (`test/client.test.mjs`, Node's built-in test runner, no dependencies) and a
`ci.yml` GitHub Actions workflow (Node 18 / 20 / 22).
- JSDoc across `src/types/*.ts` clarifying field semantics that were previously undocumented
in code (e.g. `printer_status` as an activation flag, not a connectivity check).

### Changed

- `CreatePrintJobResponse.request_timestamp` is now optional. The field is returned by the
API but is not part of the documented response contract.

## [1.0.2] — 2026-06-10

### Fixed

- Dropped `/fr/` from `expedy.io` links in the README (the site auto-localizes); fixed
Cloud Print Box and support URLs.

## [1.0.1] — 2026-06-10

### Added

- Supply-chain / provenance verification note in the README (`npm audit signatures`).

## [1.0.0] — 2026-06-05

### Added

- Initial public release: `ExpedyClient` with `printers` and `devices` resources
(`system`, `usb`, `wifi`), TypeScript types, and the full `docs/` reference.
- GitHub Actions publish workflow with npm provenance (OIDC trusted publishing).
51 changes: 51 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Contributing

Thanks for considering a contribution to `expedy-sdk-node`.

## Development

```bash
npm ci
npm run typecheck # type-check src/
npm run build # compile to dist/
npm run typecheck:examples # type-check examples/ against the compiled types
npm test # build, then run the test suite
```

The test suite (`test/*.test.mjs`) uses Node's built-in test runner against the compiled
`dist/` output — no test framework dependency. `ExpedyClient` accepts a `fetch`
implementation in its config, which the tests use to mock HTTP calls without a network
connection.

## Keeping things in sync

This repository carries three parallel descriptions of the same API surface:

- `src/types/*.ts` — the TypeScript types the SDK actually returns/accepts.
- `openapi.yaml` — the machine-readable spec, used by tooling and by other-language clients.
- `docs/api/**/*.md` — the human-readable reference.

**If you add, rename, or change the semantics of a request/response field, update all
three in the same change.** A mismatch between the SDK types and `openapi.yaml` is worse
than no spec at all.

## Style

- No comments explaining *what* code does — names should do that. JSDoc is for the *why*
or for behavior a reader could not otherwise guess (see the `printer_han` fields in
`src/types/*.ts` for the bar to meet).
- Match the existing resource/method shape in `src/resources/*.ts` when adding an endpoint:
one method per operation, `RequestOptions` as the last parameter, `encodeURIComponent`
around every path segment.
- Examples under `examples/` must be runnable as-is with
`node --experimental-strip-types examples/<name>.ts` given the right environment
variables — keep them self-contained.

## This is a public repository

Never commit real credentials, UIDs, tokens, or internal URLs. Use the placeholder values
already used throughout the codebase (`WP0RGS1SEDZ`, `MMAAZ112PI`, `example.com`, …).

## Reporting a security issue

See [SECURITY.md](SECURITY.md) — please do not open a public issue for a vulnerability.
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# expedy-sdk-node

[![npm version](https://img.shields.io/npm/v/expedy-sdk-node.svg)](https://www.npmjs.com/package/expedy-sdk-node)
[![npm downloads](https://img.shields.io/npm/dm/expedy-sdk-node.svg)](https://www.npmjs.com/package/expedy-sdk-node)
[![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/LICENSE)
[![types: TypeScript](https://img.shields.io/badge/types-TypeScript-3178c6.svg)](https://www.typescriptlang.org/)

Expand Down Expand Up @@ -42,22 +43,43 @@ console.log(`Queued job ${request_uid}`);

Full walkthrough: [docs/getting-started/quickstart.md](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/getting-started/quickstart.md).

## Chinese, Japanese, Korean

CJK text needs the `printer_han` field or it prints as `?` — no single-byte code page
carries Hanzi, Kana or Hangul, so without it every such character is silently replaced
before the job reaches the printer.

```ts
await client.printers.createPrintJob(printerUid, {
printer_msg: "<C><BOLD>주문 #1234</BOLD></C><BR><CUT/>",
printer_han: "kr", // "cn" Chinese · "kr" Korean · "jp" Japanese
});
```

Details, gotchas and examples: [docs/receipt-layout/asian-characters.md](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/receipt-layout/asian-characters.md).

## Documentation

The complete reference lives under [`docs/`](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/README.md). Key entry points:
The complete reference lives under [`docs/`](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/README.md), and the same content is published at [docs.expedy.io](https://docs.expedy.io/). Key entry points:

- [Printers vs. devices](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/concepts/printers-vs-devices.md) — which resource to use.
- [Authentication](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/getting-started/authentication.md) — `Authorization: <API_SID>:<API_TOKEN>`.
- [Create a print job](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/api/printers/create-print-job.md) — flagship endpoint.
- [Text layout tags](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/receipt-layout/text-layout-tags.md) — full tag reference.
- [Asian characters](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/receipt-layout/asian-characters.md) — `printer_han` for Chinese, Japanese, Korean.
- [Device actions](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/device-actions/autocut.md) — `<CUT/>`, `<PULSE/>`.
- [Parameter tags](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/parameter-tags/wifi.md) — Wi-Fi, NTP, APN, keep-alive, audible beep.
- [Delivery and idempotency](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/concepts/delivery-and-idempotency.md) — what `200` means, and how to avoid double prints.
- [Errors](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/getting-started/errors.md) — status codes and the `ExpedyApiError` shape.
- [Integrations index](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/integrations.md) — no-code / e-commerce / delivery platforms (Zapier, Shopify, WooCommerce, Uber Eats, …).
- [`openapi.yaml`](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/openapi.yaml) — OpenAPI 3.1 spec for all 14 endpoints.
- [`AGENTS.md`](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/AGENTS.md) — condensed reference for coding agents (Claude Code, Cursor, Copilot…).

## SDK surface

```ts
client.printers.list();
client.printers.createPrintJob(printerUid, { printer_msg, origin? });
client.printers.createPrintJob(printerUid, { printer_msg, origin?, printer_han? });

client.devices.list();
client.devices.get(deviceUid);
Expand All @@ -70,7 +92,7 @@ client.devices.system.shutdown(deviceUid);
client.devices.usb.getConfiguration(deviceUid);
client.devices.usb.scan(deviceUid);
client.devices.usb.readScan(deviceUid);
client.devices.usb.createPrintJob(deviceUid, usbPort, { usb_msg, notification_url?, origin? });
client.devices.usb.createPrintJob(deviceUid, usbPort, { usb_msg, notification_url?, origin?, printer_han? });

client.devices.wifi.getConfiguration(deviceUid);
client.devices.wifi.addSsid(deviceUid, { wifi_ssid, wifi_psk });
Expand Down
39 changes: 39 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Security Policy

## Reporting a vulnerability

Please **do not** open a public GitHub issue for a suspected security vulnerability.
Instead, report it through the
[Expedy support portal](https://help.expedy.io/support/tickets/new), or through GitHub's
[private vulnerability reporting](https://github.com/ExpedyDev/expedy-sdk-node/security/advisories/new)
if enabled on this repository.

Include enough detail to reproduce the issue: affected version, environment, and a minimal
example.

## Credentials

`apiSid` and `apiToken` (the `Authorization: <SID>:<TOKEN>` pair) are secrets:

- Store them in a secrets manager or environment variable — never in a client bundle or
committed to source control.
- Rotate the token from the [Expedy console](https://www.expedy.fr/console/) if it has ever
been logged, committed, or shared by accident.
- This repository, its `docs/` and its `examples/` never contain real credentials —
everything is a placeholder (`WP0RGS1SEDZ`, `MMAAZ112PI`, environment variable
references).

## Supply chain

Releases are published from GitHub Actions with
[npm provenance](https://docs.npmjs.com/generating-provenance-statements) — a signed
attestation linking each published version to its source commit and build. Verify it with:

```bash
npm audit signatures
```

## Supported versions

Only the latest published `1.x` release is supported. Security fixes are released as a new
patch or minor version — please upgrade rather than pinning to an old version.
Loading
Loading