Skip to content

Commit f66dd62

Browse files
tomsaasclaude
andauthored
Add printer_han (CJK) support and close SDK/docs gaps vs API v3 (#9)
* feat: add printer_han (CJK) support and close SDK/docs gaps vs API v3 Add the printer_han field (required for Chinese/Japanese/Korean text — omitting it silently replaces every such character with `?`) to CreatePrintJobRequest and CreateUsbPrintJobRequest, with a new docs/receipt-layout/asian-characters.md reference page and two runnable examples. Also close gaps found while diffing the repo against the official API docs: - request_timestamp is now optional (returned by the API but undocumented) - printer_status documented as an activation flag, not live connectivity - new docs/getting-started/errors.md and docs/concepts/delivery-and-idempotency.md - openapi.yaml (OpenAPI 3.1, all 16 operations) - test/client.test.mjs + ci.yml (Node 18/20/22) - AGENTS.md, llms.txt, CONTRIBUTING.md, SECURITY.md, CHANGELOG.md, issue/PR templates - docs/integrations.md indexing the ~140 no-code/e-commerce/delivery integrations Bumps 1.0.2 -> 1.1.0. * fix(docs): add missing /en/ locale prefix to docs.expedy.io links The user provided the site's sitemap, which confirms the canonical URL pattern is docs.expedy.io/en/<category>/<subcategory>/<slug> — the integrations index and the text-encoding-settings cross-reference were missing the locale segment. * fix(ci): unquote test glob so bash expands it, not Node's --test node --test's own glob resolution for a quoted pattern is a newer Node feature — Node 18 doesn't have it and looks for a literal file named "test/*.test.mjs", failing CI on that matrix leg. Letting bash expand the glob before Node sees it works identically on Node 18/20/22. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent d67aab8 commit f66dd62

29 files changed

Lines changed: 2170 additions & 18 deletions
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Bug report
2+
description: Something in the SDK, docs, or examples doesn't work as documented.
3+
labels: [bug]
4+
body:
5+
- type: textarea
6+
id: what-happened
7+
attributes:
8+
label: What happened?
9+
description: What you expected vs. what actually happened.
10+
validations:
11+
required: true
12+
- type: textarea
13+
id: repro
14+
attributes:
15+
label: Minimal reproduction
16+
description: >-
17+
A minimal code snippet. Do **not** include real `apiSid` / `apiToken` values or
18+
real printer / device UIDs — use placeholders.
19+
render: ts
20+
validations:
21+
required: true
22+
- type: input
23+
id: sdk-version
24+
attributes:
25+
label: expedy-sdk-node version
26+
placeholder: "1.1.0"
27+
validations:
28+
required: true
29+
- type: input
30+
id: node-version
31+
attributes:
32+
label: Node.js version
33+
placeholder: "node --version"
34+
validations:
35+
required: true
36+
- type: dropdown
37+
id: resource
38+
attributes:
39+
label: Affected resource
40+
options:
41+
- printers (cloud thermal printer)
42+
- devices (Raspberry Pi / USB)
43+
- Documentation only
44+
- Not sure
45+
validations:
46+
required: true
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Feature request
2+
description: Propose a new SDK method, type, doc page, or example.
3+
labels: [enhancement]
4+
body:
5+
- type: textarea
6+
id: problem
7+
attributes:
8+
label: What's missing?
9+
description: What are you trying to do that the SDK / docs don't currently support?
10+
validations:
11+
required: true
12+
- type: textarea
13+
id: proposal
14+
attributes:
15+
label: Proposed solution
16+
description: A method signature, a new doc page, an example — whatever fits.
17+
validations:
18+
required: false
19+
- type: textarea
20+
id: alternatives
21+
attributes:
22+
label: Alternatives considered
23+
description: Any workaround you're currently using.
24+
validations:
25+
required: false

.github/pull_request_template.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## Summary
2+
3+
<!-- What does this change do, and why? -->
4+
5+
## Checklist
6+
7+
- [ ] `npm run typecheck && npm run build && npm run typecheck:examples` pass locally
8+
- [ ] `npm test` passes locally
9+
- [ ] If a request/response field changed: `src/types/*.ts`, `openapi.yaml`, and the
10+
matching `docs/api/**/*.md` page were all updated together
11+
- [ ] No real credentials, UIDs, or internal URLs were introduced (this is a public repo)
12+
13+
## Test plan
14+
15+
<!-- How did you verify this change? -->

.github/workflows/ci.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
matrix:
13+
node-version: [18, 20, 22]
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: ${{ matrix.node-version }}
19+
- run: npm ci
20+
- run: npm run typecheck
21+
- run: npm run build
22+
- run: npm run typecheck:examples
23+
- run: node --test test/*.test.mjs

AGENTS.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# AGENTS.md
2+
3+
Guidance for coding agents (Claude Code, Cursor, GitHub Copilot, …) working in or against
4+
this repository, and a condensed reference for agents integrating `expedy-sdk-node` into
5+
someone else's codebase.
6+
7+
## What this is
8+
9+
Official Node.js SDK + API documentation for the **Expedy Print API v2**. It sends print
10+
jobs to two kinds of hardware:
11+
12+
| Resource | Hardware | Print method |
13+
| --- | --- | --- |
14+
| `printers` | Expedy cloud thermal receipt printer (own internet connection) | `client.printers.createPrintJob(printerUid, { printer_msg, ... })` |
15+
| `devices` | Raspberry Pi gateway + a third-party USB printer plugged into it | `client.devices.usb.createPrintJob(deviceUid, usbPort, { usb_msg, ... })` |
16+
17+
Read [`docs/concepts/printers-vs-devices.md`](docs/concepts/printers-vs-devices.md) before
18+
writing code against either endpoint — picking the wrong one is the most common mistake.
19+
20+
`displays` and `medias` are **out of scope** for this repository.
21+
22+
## Non-obvious things to get right
23+
24+
- **Authentication is not Bearer.** The `Authorization` header is the raw
25+
`<API_SID>:<API_TOKEN>` value, colon-separated, **no prefix**. `ExpedyClient` builds this
26+
automatically — never hand-construct the header.
27+
- **`printer_han` for Chinese/Japanese/Korean.** Without this field, CJK characters are
28+
silently replaced with `?` **before the job reaches the printer** — no error is raised.
29+
If a user asks to print non-Latin text and the code doesn't set `printer_han`, that's a
30+
bug. See [`docs/receipt-layout/asian-characters.md`](docs/receipt-layout/asian-characters.md).
31+
Values: `"cn"` Chinese, `"kr"` Korean, `"jp"` Japanese. Omit for Latin scripts.
32+
- **`200` means accepted, not printed.** Both print endpoints are asynchronous. Don't tell a
33+
user "your ticket printed" based on the SDK call resolving — see
34+
[`docs/concepts/delivery-and-idempotency.md`](docs/concepts/delivery-and-idempotency.md).
35+
- **No de-duplication.** Retrying a print request after a network error can produce two
36+
physical tickets. Track `request_uid` if you add retry logic.
37+
- **`printer_msg` / `usb_msg` carries an XML-like tag language**, not HTML — `<C>`, `<BOLD>`,
38+
`<IMG>`, `<QR>`, `<CUT/>`, `<PULSE/>`, plus one-shot provisioning tags
39+
(`<SETWIFI>`, `<SETSNTP>`, `<SETAPN>`, `<SETKEEPALIVE>`, `<UNSETBEEP/>`). Full reference:
40+
[`docs/receipt-layout/text-layout-tags.md`](docs/receipt-layout/text-layout-tags.md).
41+
- **`printer_status` is an activation flag**, not connectivity. `"0"` means suspended by
42+
ExpedyPRINT (usually billing), not "printer is offline".
43+
- Errors are `ExpedyError` (network/config) or `ExpedyApiError` (`status`, `rawBody`,
44+
`requestUid`), both exported from the package root. Always read `err.message` /
45+
`rawBody.message` rather than branching on `status` alone.
46+
47+
## Where to look
48+
49+
- **Full API reference**: [`docs/README.md`](docs/README.md) — reading order included.
50+
- **Machine-readable spec**: [`openapi.yaml`](openapi.yaml) — all 16 operations, request/
51+
response schemas, `printer_han` enum.
52+
- **Runnable examples**: [`examples/`](examples/) — one file per feature, each a complete
53+
standalone script (`node --experimental-strip-types examples/<name>.ts`).
54+
- **SDK source**: `src/client.ts` (HTTP layer, ~130 lines), `src/resources/*.ts` (one
55+
method per endpoint), `src/types/*.ts` (request/response shapes with JSDoc).
56+
- **Canonical docs site**: <https://docs.expedy.io/> — same content as `docs/`, plus
57+
hardware setup guides and ~190 integration guides out of this repo's scope (see
58+
[`docs/integrations.md`](docs/integrations.md) for the index).
59+
60+
## Working on this repository
61+
62+
- `npm run typecheck` — type-check `src/` only.
63+
- `npm run build` — compile to `dist/`.
64+
- `npm run typecheck:examples` — type-check `examples/` against the compiled types.
65+
- `npm test` — build, then run the test suite (`node --test`, no test framework
66+
dependency).
67+
- Touching a field in `src/types/*.ts`? Update the matching schema in `openapi.yaml` and the
68+
matching page under `docs/api/` in the same change — see
69+
[`CONTRIBUTING.md`](CONTRIBUTING.md).
70+
- This is a **public** repository. Never commit real credentials, UIDs, or internal URLs —
71+
use the placeholder values already used throughout `docs/` and `examples/`
72+
(`WP0RGS1SEDZ`, `MMAAZ112PI`, `example.com`, …).

CHANGELOG.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Changelog
2+
3+
All notable changes to this project are documented here. The format follows
4+
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
5+
[Semantic Versioning](https://semver.org/).
6+
7+
## [1.1.0]
8+
9+
### Added
10+
11+
- `printer_han` field on `CreatePrintJobRequest` and `CreateUsbPrintJobRequest` — required
12+
to print Chinese, Japanese or Korean text. Without it, CJK characters are silently
13+
replaced with `?` before the job reaches the printer. New `PrinterHan` /
14+
`PrinterHanScript` exported types, new
15+
[`docs/receipt-layout/asian-characters.md`](docs/receipt-layout/asian-characters.md)
16+
reference page, and two new runnable examples
17+
(`examples/receipt-asian-characters.ts`, `examples/device-rpi-usb-print-asian.ts`).
18+
- `docs/getting-started/errors.md` — SDK error types, status codes by endpoint, retry
19+
guidance.
20+
- `docs/concepts/delivery-and-idempotency.md` — what a `200` response actually guarantees,
21+
and how to avoid double prints.
22+
- `docs/integrations.md` — index of no-code / e-commerce / delivery platforms that connect
23+
to Expedy PRINT.
24+
- `openapi.yaml` — OpenAPI 3.1 description of all 16 API operations.
25+
- `AGENTS.md` and `llms.txt` for coding agents and LLM-based tools.
26+
- `CONTRIBUTING.md` and `SECURITY.md`.
27+
- Test suite (`test/client.test.mjs`, Node's built-in test runner, no dependencies) and a
28+
`ci.yml` GitHub Actions workflow (Node 18 / 20 / 22).
29+
- JSDoc across `src/types/*.ts` clarifying field semantics that were previously undocumented
30+
in code (e.g. `printer_status` as an activation flag, not a connectivity check).
31+
32+
### Changed
33+
34+
- `CreatePrintJobResponse.request_timestamp` is now optional. The field is returned by the
35+
API but is not part of the documented response contract.
36+
37+
## [1.0.2] — 2026-06-10
38+
39+
### Fixed
40+
41+
- Dropped `/fr/` from `expedy.io` links in the README (the site auto-localizes); fixed
42+
Cloud Print Box and support URLs.
43+
44+
## [1.0.1] — 2026-06-10
45+
46+
### Added
47+
48+
- Supply-chain / provenance verification note in the README (`npm audit signatures`).
49+
50+
## [1.0.0] — 2026-06-05
51+
52+
### Added
53+
54+
- Initial public release: `ExpedyClient` with `printers` and `devices` resources
55+
(`system`, `usb`, `wifi`), TypeScript types, and the full `docs/` reference.
56+
- GitHub Actions publish workflow with npm provenance (OIDC trusted publishing).

CONTRIBUTING.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Contributing
2+
3+
Thanks for considering a contribution to `expedy-sdk-node`.
4+
5+
## Development
6+
7+
```bash
8+
npm ci
9+
npm run typecheck # type-check src/
10+
npm run build # compile to dist/
11+
npm run typecheck:examples # type-check examples/ against the compiled types
12+
npm test # build, then run the test suite
13+
```
14+
15+
The test suite (`test/*.test.mjs`) uses Node's built-in test runner against the compiled
16+
`dist/` output — no test framework dependency. `ExpedyClient` accepts a `fetch`
17+
implementation in its config, which the tests use to mock HTTP calls without a network
18+
connection.
19+
20+
## Keeping things in sync
21+
22+
This repository carries three parallel descriptions of the same API surface:
23+
24+
- `src/types/*.ts` — the TypeScript types the SDK actually returns/accepts.
25+
- `openapi.yaml` — the machine-readable spec, used by tooling and by other-language clients.
26+
- `docs/api/**/*.md` — the human-readable reference.
27+
28+
**If you add, rename, or change the semantics of a request/response field, update all
29+
three in the same change.** A mismatch between the SDK types and `openapi.yaml` is worse
30+
than no spec at all.
31+
32+
## Style
33+
34+
- No comments explaining *what* code does — names should do that. JSDoc is for the *why*
35+
or for behavior a reader could not otherwise guess (see the `printer_han` fields in
36+
`src/types/*.ts` for the bar to meet).
37+
- Match the existing resource/method shape in `src/resources/*.ts` when adding an endpoint:
38+
one method per operation, `RequestOptions` as the last parameter, `encodeURIComponent`
39+
around every path segment.
40+
- Examples under `examples/` must be runnable as-is with
41+
`node --experimental-strip-types examples/<name>.ts` given the right environment
42+
variables — keep them self-contained.
43+
44+
## This is a public repository
45+
46+
Never commit real credentials, UIDs, tokens, or internal URLs. Use the placeholder values
47+
already used throughout the codebase (`WP0RGS1SEDZ`, `MMAAZ112PI`, `example.com`, …).
48+
49+
## Reporting a security issue
50+
51+
See [SECURITY.md](SECURITY.md) — please do not open a public issue for a vulnerability.

README.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# expedy-sdk-node
22

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

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

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

46+
## Chinese, Japanese, Korean
47+
48+
CJK text needs the `printer_han` field or it prints as `?` — no single-byte code page
49+
carries Hanzi, Kana or Hangul, so without it every such character is silently replaced
50+
before the job reaches the printer.
51+
52+
```ts
53+
await client.printers.createPrintJob(printerUid, {
54+
printer_msg: "<C><BOLD>주문 #1234</BOLD></C><BR><CUT/>",
55+
printer_han: "kr", // "cn" Chinese · "kr" Korean · "jp" Japanese
56+
});
57+
```
58+
59+
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).
60+
4561
## Documentation
4662

47-
The complete reference lives under [`docs/`](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/README.md). Key entry points:
63+
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:
4864

4965
- [Printers vs. devices](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/concepts/printers-vs-devices.md) — which resource to use.
5066
- [Authentication](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/getting-started/authentication.md)`Authorization: <API_SID>:<API_TOKEN>`.
5167
- [Create a print job](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/api/printers/create-print-job.md) — flagship endpoint.
5268
- [Text layout tags](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/receipt-layout/text-layout-tags.md) — full tag reference.
69+
- [Asian characters](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/receipt-layout/asian-characters.md)`printer_han` for Chinese, Japanese, Korean.
5370
- [Device actions](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/device-actions/autocut.md)`<CUT/>`, `<PULSE/>`.
5471
- [Parameter tags](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/parameter-tags/wifi.md) — Wi-Fi, NTP, APN, keep-alive, audible beep.
72+
- [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.
73+
- [Errors](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/docs/getting-started/errors.md) — status codes and the `ExpedyApiError` shape.
74+
- [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, …).
75+
- [`openapi.yaml`](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/openapi.yaml) — OpenAPI 3.1 spec for all 14 endpoints.
76+
- [`AGENTS.md`](https://github.com/ExpedyDev/expedy-sdk-node/blob/main/AGENTS.md) — condensed reference for coding agents (Claude Code, Cursor, Copilot…).
5577

5678
## SDK surface
5779

5880
```ts
5981
client.printers.list();
60-
client.printers.createPrintJob(printerUid, { printer_msg, origin? });
82+
client.printers.createPrintJob(printerUid, { printer_msg, origin?, printer_han? });
6183

6284
client.devices.list();
6385
client.devices.get(deviceUid);
@@ -70,7 +92,7 @@ client.devices.system.shutdown(deviceUid);
7092
client.devices.usb.getConfiguration(deviceUid);
7193
client.devices.usb.scan(deviceUid);
7294
client.devices.usb.readScan(deviceUid);
73-
client.devices.usb.createPrintJob(deviceUid, usbPort, { usb_msg, notification_url?, origin? });
95+
client.devices.usb.createPrintJob(deviceUid, usbPort, { usb_msg, notification_url?, origin?, printer_han? });
7496

7597
client.devices.wifi.getConfiguration(deviceUid);
7698
client.devices.wifi.addSsid(deviceUid, { wifi_ssid, wifi_psk });

SECURITY.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Security Policy
2+
3+
## Reporting a vulnerability
4+
5+
Please **do not** open a public GitHub issue for a suspected security vulnerability.
6+
Instead, report it through the
7+
[Expedy support portal](https://help.expedy.io/support/tickets/new), or through GitHub's
8+
[private vulnerability reporting](https://github.com/ExpedyDev/expedy-sdk-node/security/advisories/new)
9+
if enabled on this repository.
10+
11+
Include enough detail to reproduce the issue: affected version, environment, and a minimal
12+
example.
13+
14+
## Credentials
15+
16+
`apiSid` and `apiToken` (the `Authorization: <SID>:<TOKEN>` pair) are secrets:
17+
18+
- Store them in a secrets manager or environment variable — never in a client bundle or
19+
committed to source control.
20+
- Rotate the token from the [Expedy console](https://www.expedy.fr/console/) if it has ever
21+
been logged, committed, or shared by accident.
22+
- This repository, its `docs/` and its `examples/` never contain real credentials —
23+
everything is a placeholder (`WP0RGS1SEDZ`, `MMAAZ112PI`, environment variable
24+
references).
25+
26+
## Supply chain
27+
28+
Releases are published from GitHub Actions with
29+
[npm provenance](https://docs.npmjs.com/generating-provenance-statements) — a signed
30+
attestation linking each published version to its source commit and build. Verify it with:
31+
32+
```bash
33+
npm audit signatures
34+
```
35+
36+
## Supported versions
37+
38+
Only the latest published `1.x` release is supported. Security fixes are released as a new
39+
patch or minor version — please upgrade rather than pinning to an old version.

0 commit comments

Comments
 (0)