@w6w/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/mod.d.ts +37 -0
  4. package/dist/mod.d.ts.map +1 -0
  5. package/dist/mod.js +51 -0
  6. package/dist/mod.js.map +1 -0
  7. package/dist/src/client.d.ts +134 -0
  8. package/dist/src/client.d.ts.map +1 -0
  9. package/dist/src/client.js +165 -0
  10. package/dist/src/client.js.map +1 -0
  11. package/dist/src/config.d.ts +151 -0
  12. package/dist/src/config.d.ts.map +1 -0
  13. package/dist/src/config.js +161 -0
  14. package/dist/src/config.js.map +1 -0
  15. package/dist/src/connections.d.ts +58 -0
  16. package/dist/src/connections.d.ts.map +1 -0
  17. package/dist/src/connections.js +52 -0
  18. package/dist/src/connections.js.map +1 -0
  19. package/dist/src/documents.d.ts +168 -0
  20. package/dist/src/documents.d.ts.map +1 -0
  21. package/dist/src/documents.js +178 -0
  22. package/dist/src/documents.js.map +1 -0
  23. package/dist/src/env.d.ts +61 -0
  24. package/dist/src/env.d.ts.map +1 -0
  25. package/dist/src/env.js +73 -0
  26. package/dist/src/env.js.map +1 -0
  27. package/dist/src/errors.d.ts +79 -0
  28. package/dist/src/errors.d.ts.map +1 -0
  29. package/dist/src/errors.js +88 -0
  30. package/dist/src/errors.js.map +1 -0
  31. package/dist/src/http.d.ts +130 -0
  32. package/dist/src/http.d.ts.map +1 -0
  33. package/dist/src/http.js +150 -0
  34. package/dist/src/http.js.map +1 -0
  35. package/dist/src/me.d.ts +53 -0
  36. package/dist/src/me.d.ts.map +1 -0
  37. package/dist/src/me.js +77 -0
  38. package/dist/src/me.js.map +1 -0
  39. package/dist/src/run.d.ts +92 -0
  40. package/dist/src/run.d.ts.map +1 -0
  41. package/dist/src/run.js +83 -0
  42. package/dist/src/run.js.map +1 -0
  43. package/dist/src/types.d.ts +362 -0
  44. package/dist/src/types.d.ts.map +1 -0
  45. package/dist/src/types.js +109 -0
  46. package/dist/src/types.js.map +1 -0
  47. package/dist/src/vars.d.ts +149 -0
  48. package/dist/src/vars.d.ts.map +1 -0
  49. package/dist/src/vars.js +149 -0
  50. package/dist/src/vars.js.map +1 -0
  51. package/dist/src/version.d.ts +14 -0
  52. package/dist/src/version.d.ts.map +1 -0
  53. package/dist/src/version.js +14 -0
  54. package/dist/src/version.js.map +1 -0
  55. package/dist/src/workflows.d.ts +180 -0
  56. package/dist/src/workflows.d.ts.map +1 -0
  57. package/dist/src/workflows.js +160 -0
  58. package/dist/src/workflows.js.map +1 -0
  59. package/package.json +36 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * `client.documents.*` — the document store, addressed the way the server
3
+ * addresses it.
4
+ *
5
+ * Six operations, one HTTP call each. **Create by `key`, read by id or by key,
6
+ * update and delete by id** (`docs/implementation.md` §7, D6 amended by D12).
7
+ * There is no key-addressed façade over the id routes and no client-side
8
+ * list-then-filter anywhere in this file: a `getByKey` implemented as "list
9
+ * everything and scan" is O(n) over someone's whole store, it races, and a
10
+ * wrapper that has to compose two calls is describing an operation that belongs
11
+ * in the API.
12
+ *
13
+ * `getByKey` therefore targets a real route — `GET /documents/by-key/{key}`
14
+ * (verified live 2026-07-28).
15
+ *
16
+ * Documents are **project-scoped**: every route below accepts an optional
17
+ * `?project=`, resolved per call, then from the client's default, and otherwise
18
+ * left to the server (which picks the account's default project). Vars are not
19
+ * — see `src/vars.ts`.
20
+ *
21
+ * @module
22
+ */
23
+ import { path } from "./http.js";
24
+ import { unwrap } from "./types.js";
25
+ /**
26
+ * The `documents` namespace on a `W6wClient`.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const doc = await client.documents.create({ key: "welcome", content: "# Hi" });
31
+ * await client.documents.update(doc.id, { content: "# Hello" });
32
+ * await client.documents.delete(doc.id);
33
+ * ```
34
+ */
35
+ export class DocumentsApi {
36
+ #host;
37
+ /**
38
+ * @param host - The client this namespace issues requests through.
39
+ */
40
+ constructor(host) {
41
+ this.#host = host;
42
+ }
43
+ /**
44
+ * The project this call is scoped to: per-call argument first, then the
45
+ * client's default, then nothing at all — an absent value means "no
46
+ * `?project=`", which is how the caller asks for the account's default.
47
+ */
48
+ #project(options) {
49
+ return options?.project ?? this.#host.config.project ?? undefined;
50
+ }
51
+ /**
52
+ * List the caller's documents.
53
+ *
54
+ * @param options - Optional per-call project scope.
55
+ * @returns The documents, unwrapped from the `documents` envelope.
56
+ * @throws {ApiError} On any non-2xx, e.g. `400 unknown_project`.
57
+ */
58
+ async list(options) {
59
+ const res = await this.#host.request({
60
+ method: "GET",
61
+ path: "/documents",
62
+ query: { project: this.#project(options) },
63
+ });
64
+ return unwrap(res, "documents");
65
+ }
66
+ /**
67
+ * Fetch one document by its server-issued id.
68
+ *
69
+ * @param id - The `doc_…` id.
70
+ * @param options - Optional per-call project scope.
71
+ * @returns The document.
72
+ * @throws {ApiError} `404 unknown_document` when there is no such id.
73
+ */
74
+ async get(id, options) {
75
+ const res = await this.#host.request({
76
+ method: "GET",
77
+ path: path `/documents/${id}`,
78
+ query: { project: this.#project(options) },
79
+ });
80
+ return unwrap(res, "document");
81
+ }
82
+ /**
83
+ * Fetch one document by the `key` its author chose.
84
+ *
85
+ * The key is percent-encoded at interpolation by the `path` tag, which is not
86
+ * ceremony: the server accepts any non-empty key up to 128 characters, so
87
+ * `"a/b"` and `".."` are keys a user can legitimately create, and concatenated
88
+ * naively they address a *different route* — `".."` resolves to the list
89
+ * endpoint and returns every document with a `200` and no error anywhere.
90
+ *
91
+ * Encoding, never validation: a key the server accepts is sent as-is
92
+ * (encoded). This method does not reject, trim or canonicalise a key —
93
+ * refusing one locally would make a document that exists permanently
94
+ * unreadable through this client.
95
+ *
96
+ * @param key - The document key, sent encoded and otherwise untouched.
97
+ * @param options - Optional per-call project scope.
98
+ * @returns The document.
99
+ * @throws {ApiError} `404 unknown_document` when there is no such key.
100
+ */
101
+ async getByKey(key, options) {
102
+ const res = await this.#host.request({
103
+ method: "GET",
104
+ path: path `/documents/by-key/${key}`,
105
+ query: { project: this.#project(options) },
106
+ });
107
+ return unwrap(res, "document");
108
+ }
109
+ /**
110
+ * Create a document under a caller-chosen key.
111
+ *
112
+ * @param input - `key` and `content` are required; `format` defaults server-side to `"text"`.
113
+ * @param options - Optional per-call project scope.
114
+ * @returns The created document (the server answers `201`).
115
+ * @throws {ApiError} `409 document_exists` on a duplicate key — the code reaches the caller intact.
116
+ */
117
+ async create(input, options) {
118
+ const res = await this.#host.request({
119
+ method: "POST",
120
+ path: "/documents",
121
+ query: { project: this.#project(options) },
122
+ // Assembled field by field rather than forwarded wholesale: the body that
123
+ // goes on the wire is exactly the four documented fields, and `undefined`
124
+ // ones are dropped by JSON serialisation.
125
+ body: {
126
+ key: input.key,
127
+ content: input.content,
128
+ format: input.format,
129
+ description: input.description,
130
+ },
131
+ });
132
+ return unwrap(res, "document");
133
+ }
134
+ /**
135
+ * Patch a document's content, format or description.
136
+ *
137
+ * Addressed by id, never by key, and the key itself cannot be changed.
138
+ *
139
+ * @param id - The `doc_…` id.
140
+ * @param patch - The fields to change; omitted fields are left alone.
141
+ * @param options - Optional per-call project scope.
142
+ * @returns The updated document.
143
+ * @throws {ApiError} `404 unknown_document` when there is no such id.
144
+ */
145
+ async update(id, patch, options) {
146
+ const res = await this.#host.request({
147
+ method: "PATCH",
148
+ path: path `/documents/${id}`,
149
+ query: { project: this.#project(options) },
150
+ body: {
151
+ content: patch.content,
152
+ format: patch.format,
153
+ description: patch.description,
154
+ },
155
+ });
156
+ return unwrap(res, "document");
157
+ }
158
+ /**
159
+ * Delete a document.
160
+ *
161
+ * Returns nothing: the server's `{ok: true}` carries no information a caller
162
+ * can use, and unwrapping it to a value would invite `if (await delete(...))`.
163
+ * Deleting an unknown id is `404 unknown_document`, **not** a silent success —
164
+ * the delete is not idempotent and this method does not pretend otherwise.
165
+ *
166
+ * @param id - The `doc_…` id.
167
+ * @param options - Optional per-call project scope.
168
+ * @throws {ApiError} `404 unknown_document` when there is no such id.
169
+ */
170
+ async delete(id, options) {
171
+ await this.#host.request({
172
+ method: "DELETE",
173
+ path: path `/documents/${id}`,
174
+ query: { project: this.#project(options) },
175
+ });
176
+ }
177
+ }
178
+ //# sourceMappingURL=documents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documents.js","sourceRoot":"","sources":["../../src/documents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The **only** module in this package that reads the environment.
3
+ *
4
+ * `docs/implementation.md` §2 (MECHANISM PIN — instance state, never globals)
5
+ * pins exactly one environment-reading module per wrapper; everything else
6
+ * receives resolved configuration as a value. That is not a style preference:
7
+ * it is what keeps `Deno.env` / `process.env` out of the operation modules, so
8
+ * no operation can silently acquire a dependency on ambient state and every
9
+ * env-var behaviour has exactly one seam to exercise in tests (§9).
10
+ *
11
+ * The spec names that module `src/config.ts`; this package splits the *reading*
12
+ * (here) from the *resolving* (`src/config.ts`, which imports this file and is
13
+ * the only caller). The invariant the pin is about — one place touches the
14
+ * environment — is unchanged, and it is enforced mechanically: the repo gate
15
+ * greps the whole `src/` tree for `Deno.env|process.env` and requires exactly
16
+ * one matching file.
17
+ *
18
+ * The read is a **capability probe**, not a runtime assumption, so the same
19
+ * source runs under Deno, Node and Bun (`docs/implementation.md` §1: the client
20
+ * core is runtime-neutral TypeScript over Web-standard globals). Where neither
21
+ * runtime object is present the value degrades to "unset" rather than throwing —
22
+ * a browser bundle of this package must still construct a client from explicit
23
+ * arguments.
24
+ *
25
+ * @module
26
+ */
27
+ /** Environment variable holding the server **origin** (see `src/config.ts`). */
28
+ export declare const ENV_BASE_URL = "W6W_BASE_URL";
29
+ /** Environment variable holding the bearer token. */
30
+ export declare const ENV_TOKEN = "W6W_TOKEN";
31
+ /**
32
+ * Read one environment variable, or `undefined` when it carries no usable
33
+ * value.
34
+ *
35
+ * **A variable that is set but empty — or whitespace-only — reads as ABSENT.**
36
+ * `W6W_BASE_URL=`, `W6W_BASE_URL=" "` and an unset `W6W_BASE_URL` are all the
37
+ * same thing here, and the precedence chain falls through all three to the next
38
+ * source (and ultimately to a configuration error).
39
+ *
40
+ * This deviates deliberately from the pinned snippet in
41
+ * `docs/implementation.md` §2, which reads `g.Deno.env.get(name) ?? undefined`.
42
+ * A nullish coalesce does **not** convert `""` to `undefined`, so an empty
43
+ * `W6W_BASE_URL` would survive as a value, reach the join rule, and resolve to
44
+ * the relative URL `"/api"` — exactly the browser same-origin default that §2
45
+ * forbids a library from having. A relative base URL fails later, somewhere
46
+ * else, with a message about the request rather than about the configuration.
47
+ *
48
+ * So the check below is a **truthiness/trim** check and not a nullish one, on
49
+ * purpose. Do not "tidy" it back to `??`. An empty-string environment variable
50
+ * is not a hypothetical: a Docker `ENV`/`--build-arg` that was never given a
51
+ * value produces exactly this, and it looks absent in every log line while
52
+ * behaving as present in every `??`.
53
+ *
54
+ * The value is returned verbatim (not trimmed) — normalising the *shape* of a
55
+ * base URL belongs to `src/config.ts`, which owns the join rule.
56
+ *
57
+ * @param name - The variable to read, e.g. {@linkcode ENV_BASE_URL}.
58
+ * @returns The value, or `undefined` if unset, empty, blank or unavailable.
59
+ */
60
+ export declare function readEnv(name: string): string | undefined;
61
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,gFAAgF;AAChF,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,qDAAqD;AACrD,eAAO,MAAM,SAAS,cAAc,CAAC;AAarC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQxD"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The **only** module in this package that reads the environment.
3
+ *
4
+ * `docs/implementation.md` §2 (MECHANISM PIN — instance state, never globals)
5
+ * pins exactly one environment-reading module per wrapper; everything else
6
+ * receives resolved configuration as a value. That is not a style preference:
7
+ * it is what keeps `Deno.env` / `process.env` out of the operation modules, so
8
+ * no operation can silently acquire a dependency on ambient state and every
9
+ * env-var behaviour has exactly one seam to exercise in tests (§9).
10
+ *
11
+ * The spec names that module `src/config.ts`; this package splits the *reading*
12
+ * (here) from the *resolving* (`src/config.ts`, which imports this file and is
13
+ * the only caller). The invariant the pin is about — one place touches the
14
+ * environment — is unchanged, and it is enforced mechanically: the repo gate
15
+ * greps the whole `src/` tree for `Deno.env|process.env` and requires exactly
16
+ * one matching file.
17
+ *
18
+ * The read is a **capability probe**, not a runtime assumption, so the same
19
+ * source runs under Deno, Node and Bun (`docs/implementation.md` §1: the client
20
+ * core is runtime-neutral TypeScript over Web-standard globals). Where neither
21
+ * runtime object is present the value degrades to "unset" rather than throwing —
22
+ * a browser bundle of this package must still construct a client from explicit
23
+ * arguments.
24
+ *
25
+ * @module
26
+ */
27
+ /** Environment variable holding the server **origin** (see `src/config.ts`). */
28
+ export const ENV_BASE_URL = "W6W_BASE_URL";
29
+ /** Environment variable holding the bearer token. */
30
+ export const ENV_TOKEN = "W6W_TOKEN";
31
+ /**
32
+ * Read one environment variable, or `undefined` when it carries no usable
33
+ * value.
34
+ *
35
+ * **A variable that is set but empty — or whitespace-only — reads as ABSENT.**
36
+ * `W6W_BASE_URL=`, `W6W_BASE_URL=" "` and an unset `W6W_BASE_URL` are all the
37
+ * same thing here, and the precedence chain falls through all three to the next
38
+ * source (and ultimately to a configuration error).
39
+ *
40
+ * This deviates deliberately from the pinned snippet in
41
+ * `docs/implementation.md` §2, which reads `g.Deno.env.get(name) ?? undefined`.
42
+ * A nullish coalesce does **not** convert `""` to `undefined`, so an empty
43
+ * `W6W_BASE_URL` would survive as a value, reach the join rule, and resolve to
44
+ * the relative URL `"/api"` — exactly the browser same-origin default that §2
45
+ * forbids a library from having. A relative base URL fails later, somewhere
46
+ * else, with a message about the request rather than about the configuration.
47
+ *
48
+ * So the check below is a **truthiness/trim** check and not a nullish one, on
49
+ * purpose. Do not "tidy" it back to `??`. An empty-string environment variable
50
+ * is not a hypothetical: a Docker `ENV`/`--build-arg` that was never given a
51
+ * value produces exactly this, and it looks absent in every log line while
52
+ * behaving as present in every `??`.
53
+ *
54
+ * The value is returned verbatim (not trimmed) — normalising the *shape* of a
55
+ * base URL belongs to `src/config.ts`, which owns the join rule.
56
+ *
57
+ * @param name - The variable to read, e.g. {@linkcode ENV_BASE_URL}.
58
+ * @returns The value, or `undefined` if unset, empty, blank or unavailable.
59
+ */
60
+ export function readEnv(name) {
61
+ const g = globalThis;
62
+ let value;
63
+ if (g.Deno?.env?.get)
64
+ value = g.Deno.env.get(name);
65
+ else if (g.process?.env)
66
+ value = g.process.env[name];
67
+ else
68
+ return undefined;
69
+ if (value === undefined || value.trim().length === 0)
70
+ return undefined;
71
+ return value;
72
+ }
73
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAE3C,qDAAqD;AACrD,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;AAarC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,MAAM,CAAC,GAAG,UAAyC,CAAC;IACpD,IAAI,KAAyB,CAAC;IAC9B,IAAI,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG;QAAE,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;SAC9C,IAAI,CAAC,CAAC,OAAO,EAAE,GAAG;QAAE,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;;QAChD,OAAO,SAAS,CAAC;IACtB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACvE,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The package's error types.
3
+ *
4
+ * Two of them, and the split is deliberate:
5
+ *
6
+ * - {@linkcode ApiError} — the server answered (or could not be reached at all).
7
+ * Four fields, pinned identically in all three w6w wrappers by
8
+ * `docs/implementation.md` §3, and raised by exactly the three failure modes
9
+ * in `src/http.ts`.
10
+ * - {@linkcode ConfigError} — the client was never in a position to make a
11
+ * request: no base URL, or no token. No HTTP exchange happened, so an
12
+ * `ApiError`'s `status` and `code` would have to be invented; `status: 0` in
13
+ * particular is already spoken for by `network_error`, and conflating "your
14
+ * server is unreachable" with "you have not configured a server" is exactly
15
+ * the diagnosis a user needs kept apart.
16
+ *
17
+ * Every public member carries an explicit type annotation. That is a
18
+ * publishing requirement, not a preference: JSR publishes this file as
19
+ * TypeScript source and rejects inferred public types ("no slow types",
20
+ * `docs/implementation.md` §1).
21
+ *
22
+ * @module
23
+ */
24
+ /**
25
+ * An error raised by a w6w API call.
26
+ *
27
+ * `raw` is the **parsed response body** when the server sent one, otherwise
28
+ * `null`. It is kept because an error body carries fields the message alone
29
+ * drops — an invoke failure rides alongside `logs` and `apiCalls` — and because
30
+ * a caller inspecting a server error should never have to re-issue the request
31
+ * to see what came back.
32
+ *
33
+ * Classification is by {@linkcode status} plus a **prefix** of
34
+ * {@linkcode code} — `unknown_*` (404), `invalid_*` (400), `*_exists` (409) —
35
+ * never by an exhaustive list of code strings. The server mints codes freely
36
+ * and a closed list in three wrappers would be stale within a release
37
+ * (`docs/implementation.md` §3).
38
+ *
39
+ * One status deserves singling out: **`424` is an app/upstream failure in the
40
+ * execute phase**, and it is a 4xx rather than a 5xx on purpose — Cloudflare
41
+ * replaces an origin 5xx with its own CORS-less HTML page, which would strip
42
+ * the real message. Do not normalise a 424 into a server error or into a
43
+ * transport failure; pass it through with its code and body intact.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * try {
48
+ * await client.request({ method: "GET", path: "/vars/var_missing" });
49
+ * } catch (err) {
50
+ * if (err instanceof ApiError && err.status === 404) {
51
+ * // err.code === "unknown_var", err.raw === { error: { … } }
52
+ * }
53
+ * }
54
+ * ```
55
+ */
56
+ export declare class ApiError extends Error {
57
+ /** HTTP status, or `0` when the request never produced a response. */
58
+ readonly status: number;
59
+ /** Server-minted error code, `"network_error"`, or `"bad_response"`. */
60
+ readonly code: string;
61
+ /** The parsed response body, when there was one; otherwise `null`. */
62
+ readonly raw: unknown;
63
+ constructor(status: number, code: string, message: string, raw?: unknown);
64
+ }
65
+ /**
66
+ * The client is not configured well enough to make a request.
67
+ *
68
+ * Raised at construction when there is no base URL, and on the first request
69
+ * when there is no token — a client with no token can still be *built*, so a
70
+ * CLI's `--help` and `--version` work offline (`docs/implementation.md` §2).
71
+ *
72
+ * The message always names the environment variable that would have supplied
73
+ * the missing value, because "which variable?" is the only question a user has
74
+ * at this point.
75
+ */
76
+ export declare class ConfigError extends Error {
77
+ constructor(message: string);
78
+ }
79
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;gBAEV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,OAAc;CAO/E;AAED;;;;;;;;;;GAUG;AACH,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The package's error types.
3
+ *
4
+ * Two of them, and the split is deliberate:
5
+ *
6
+ * - {@linkcode ApiError} — the server answered (or could not be reached at all).
7
+ * Four fields, pinned identically in all three w6w wrappers by
8
+ * `docs/implementation.md` §3, and raised by exactly the three failure modes
9
+ * in `src/http.ts`.
10
+ * - {@linkcode ConfigError} — the client was never in a position to make a
11
+ * request: no base URL, or no token. No HTTP exchange happened, so an
12
+ * `ApiError`'s `status` and `code` would have to be invented; `status: 0` in
13
+ * particular is already spoken for by `network_error`, and conflating "your
14
+ * server is unreachable" with "you have not configured a server" is exactly
15
+ * the diagnosis a user needs kept apart.
16
+ *
17
+ * Every public member carries an explicit type annotation. That is a
18
+ * publishing requirement, not a preference: JSR publishes this file as
19
+ * TypeScript source and rejects inferred public types ("no slow types",
20
+ * `docs/implementation.md` §1).
21
+ *
22
+ * @module
23
+ */
24
+ /**
25
+ * An error raised by a w6w API call.
26
+ *
27
+ * `raw` is the **parsed response body** when the server sent one, otherwise
28
+ * `null`. It is kept because an error body carries fields the message alone
29
+ * drops — an invoke failure rides alongside `logs` and `apiCalls` — and because
30
+ * a caller inspecting a server error should never have to re-issue the request
31
+ * to see what came back.
32
+ *
33
+ * Classification is by {@linkcode status} plus a **prefix** of
34
+ * {@linkcode code} — `unknown_*` (404), `invalid_*` (400), `*_exists` (409) —
35
+ * never by an exhaustive list of code strings. The server mints codes freely
36
+ * and a closed list in three wrappers would be stale within a release
37
+ * (`docs/implementation.md` §3).
38
+ *
39
+ * One status deserves singling out: **`424` is an app/upstream failure in the
40
+ * execute phase**, and it is a 4xx rather than a 5xx on purpose — Cloudflare
41
+ * replaces an origin 5xx with its own CORS-less HTML page, which would strip
42
+ * the real message. Do not normalise a 424 into a server error or into a
43
+ * transport failure; pass it through with its code and body intact.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * try {
48
+ * await client.request({ method: "GET", path: "/vars/var_missing" });
49
+ * } catch (err) {
50
+ * if (err instanceof ApiError && err.status === 404) {
51
+ * // err.code === "unknown_var", err.raw === { error: { … } }
52
+ * }
53
+ * }
54
+ * ```
55
+ */
56
+ export class ApiError extends Error {
57
+ /** HTTP status, or `0` when the request never produced a response. */
58
+ status;
59
+ /** Server-minted error code, `"network_error"`, or `"bad_response"`. */
60
+ code;
61
+ /** The parsed response body, when there was one; otherwise `null`. */
62
+ raw;
63
+ constructor(status, code, message, raw = null) {
64
+ super(message);
65
+ this.name = "ApiError";
66
+ this.status = status;
67
+ this.code = code;
68
+ this.raw = raw;
69
+ }
70
+ }
71
+ /**
72
+ * The client is not configured well enough to make a request.
73
+ *
74
+ * Raised at construction when there is no base URL, and on the first request
75
+ * when there is no token — a client with no token can still be *built*, so a
76
+ * CLI's `--help` and `--version` work offline (`docs/implementation.md` §2).
77
+ *
78
+ * The message always names the environment variable that would have supplied
79
+ * the missing value, because "which variable?" is the only question a user has
80
+ * at this point.
81
+ */
82
+ export class ConfigError extends Error {
83
+ constructor(message) {
84
+ super(message);
85
+ this.name = "ConfigError";
86
+ }
87
+ }
88
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,sEAAsE;IAC7D,MAAM,CAAS;IACxB,wEAAwE;IAC/D,IAAI,CAAS;IACtB,sEAAsE;IAC7D,GAAG,CAAU;IAEtB,YAAY,MAAc,EAAE,IAAY,EAAE,OAAe,EAAE,MAAe,IAAI;QAC5E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF;AAED;;;;;;;;;;GAUG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The transport: one `request()`, three failure modes, no policy.
3
+ *
4
+ * Everything the operation modules do goes through {@linkcode request}. It
5
+ * attaches the bearer, serialises a JSON body, reads the response exactly once,
6
+ * parses it guardedly, and raises one of the three failure modes pinned in
7
+ * `docs/implementation.md` §3 — and nothing else. In particular it does **not**
8
+ * retry, does **not** refresh a token, does **not** poll, and does **not**
9
+ * inspect an error code to decide on a side effect. A `401` raises and stops.
10
+ *
11
+ * It also returns the **HTTP status** alongside the parsed body, because a
12
+ * `202` is a normal outcome on this API (a queued workflow run), not an error —
13
+ * and a signature that dropped the status would leave the run operations unable
14
+ * to tell "finished" from "queued" (§4).
15
+ *
16
+ * @module
17
+ */
18
+ import { type FetchLike, type ResolvedConfig } from "./config.ts";
19
+ /** HTTP methods this surface uses. */
20
+ export type HttpMethod = "GET" | "POST" | "PATCH" | "DELETE";
21
+ /**
22
+ * Query parameters. `undefined` values are dropped, so an optional argument
23
+ * can be forwarded without a conditional at the call site. Values are
24
+ * percent-encoded by `URLSearchParams`.
25
+ */
26
+ export type QueryParams = Record<string, string | number | boolean | undefined>;
27
+ /** One request, fully described. */
28
+ export interface RequestOptions {
29
+ /** HTTP method. */
30
+ method: HttpMethod;
31
+ /**
32
+ * Base-relative path with a leading slash, e.g. `/documents`. Build it with
33
+ * the {@linkcode path} tag whenever any part of it comes from the caller.
34
+ */
35
+ path: string;
36
+ /** Query parameters, if any. */
37
+ query?: QueryParams;
38
+ /** Request body. Serialised as JSON when present; omit it for a bodiless request. */
39
+ body?: unknown;
40
+ }
41
+ /**
42
+ * A successful response: the parsed body plus the status that carried it.
43
+ *
44
+ * `body` is `null` for an empty body — a `204`-style response must not crash a
45
+ * caller, and the server's `{ok:true}` deletes carry nothing an operation needs.
46
+ */
47
+ export interface HttpResponse<T> {
48
+ status: number;
49
+ body: T;
50
+ }
51
+ /**
52
+ * Build a base-relative path, percent-encoding every interpolated value.
53
+ *
54
+ * ```ts
55
+ * path`/documents/by-key/${key}` // key "a/b" → "/documents/by-key/a%2Fb"
56
+ * ```
57
+ *
58
+ * **Use this tag for every path that contains a caller-supplied value**, per the
59
+ * encoding pin in `docs/implementation.md` §2. It exists as a tag rather than a
60
+ * helper function because the encoding then happens at the point of
61
+ * interpolation and cannot be forgotten one call site at a time: there is no way
62
+ * to write the template and skip it.
63
+ *
64
+ * Why it matters, in one line: the server accepts any non-empty key up to 128
65
+ * characters, so `".."` is a key a user can legitimately create — and
66
+ * `` `/documents/by-key/${".."}` `` naively concatenated resolves to the
67
+ * **list** route, returning every document with a `200` and no error anywhere.
68
+ *
69
+ * This is encoding, never validation. A key the server accepts is sent as-is
70
+ * (encoded); the wrapper does not reject, canonicalise or rewrite it. A
71
+ * character policy for keys is business logic, and it lives in the server.
72
+ *
73
+ * The one case encoding cannot fix is a dot-only segment: `.` is unreserved in
74
+ * RFC 3986, and the URL parser behind `fetch` removes dot segments regardless of
75
+ * how they are spelled. `"."` and `".."` are therefore not addressable through a
76
+ * path segment from this runtime — encode and send anyway, and let the server
77
+ * decide what it means.
78
+ *
79
+ * @param strings - Template literal fragments (author-controlled, not encoded).
80
+ * @param values - Interpolated values (caller-controlled, each percent-encoded).
81
+ * @returns The assembled path.
82
+ */
83
+ export declare function path(strings: TemplateStringsArray, ...values: string[]): string;
84
+ /**
85
+ * Assemble the full request URL from a resolved base, a path and query params.
86
+ *
87
+ * The base is already joined with the API base path by `src/config.ts`; the
88
+ * path is appended verbatim (it is expected to be encoded already — see
89
+ * {@linkcode path}), and the query string is built with `URLSearchParams`,
90
+ * which encodes both names and values.
91
+ *
92
+ * @param config - The resolved configuration.
93
+ * @param options - The request being built.
94
+ * @returns The absolute URL to fetch.
95
+ */
96
+ export declare function buildUrl(config: ResolvedConfig, options: RequestOptions): string;
97
+ /**
98
+ * Perform one request and map its outcome.
99
+ *
100
+ * Returns `{status, body}` on any `2xx`. Raises {@linkcode ApiError} in exactly
101
+ * three cases, transcribed from the studio's client, which has these three and
102
+ * no others:
103
+ *
104
+ * 1. **Transport failure** — `fetch` itself rejected: server down, DNS, TLS,
105
+ * connection refused. `status` `0`, code `network_error`, and the message
106
+ * names the **method and full URL** plus the underlying message, because a
107
+ * bare `TypeError: Failed to fetch` says nothing a user can act on.
108
+ * 2. **Non-JSON body on a non-OK status** — a proxy or Cloudflare HTML error
109
+ * page. `code` `bad_response`, message carrying a ≤200-character snippet;
110
+ * reporting it as an opaque `SyntaxError` would throw away the only
111
+ * diagnostic present. A non-JSON body on an **OK** status is not an error.
112
+ * 3. **Envelope error** — a non-OK status with a JSON body. `code` from
113
+ * `body.error.code` (else `"error"`), `message` from `body.error.message`
114
+ * (else the status text), and `raw` = the parsed body, which carries the
115
+ * fields an invoke failure rides alongside (`logs`, `apiCalls`).
116
+ *
117
+ * A `424` lands in case 3 like any other non-OK JSON status: it means the
118
+ * target app or its upstream vendor failed during execute, and it is
119
+ * deliberately a 4xx so Cloudflare cannot swallow it. It is never a transport
120
+ * error and is never normalised into a 5xx.
121
+ *
122
+ * @param config - Resolved configuration (base URL and credential).
123
+ * @param fetchImpl - The transport to use; injected, never read from a module global.
124
+ * @param options - The request.
125
+ * @returns The status and parsed body.
126
+ * @throws {ConfigError} When no token is configured.
127
+ * @throws {ApiError} On any of the three failure modes above.
128
+ */
129
+ export declare function request<T>(config: ResolvedConfig, fetchImpl: FetchLike, options: RequestOptions): Promise<HttpResponse<T>>;
130
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,KAAK,SAAS,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAGhF,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE7D;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAEhF,oCAAoC;AACpC,MAAM,WAAW,cAAc;IAC7B,mBAAmB;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,qFAAqF;IACrF,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;CACT;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAM/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAOhF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAwD1B"}