@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.
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/mod.d.ts +37 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +51 -0
- package/dist/mod.js.map +1 -0
- package/dist/src/client.d.ts +134 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +165 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/config.d.ts +151 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +161 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/connections.d.ts +58 -0
- package/dist/src/connections.d.ts.map +1 -0
- package/dist/src/connections.js +52 -0
- package/dist/src/connections.js.map +1 -0
- package/dist/src/documents.d.ts +168 -0
- package/dist/src/documents.d.ts.map +1 -0
- package/dist/src/documents.js +178 -0
- package/dist/src/documents.js.map +1 -0
- package/dist/src/env.d.ts +61 -0
- package/dist/src/env.d.ts.map +1 -0
- package/dist/src/env.js +73 -0
- package/dist/src/env.js.map +1 -0
- package/dist/src/errors.d.ts +79 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +88 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/http.d.ts +130 -0
- package/dist/src/http.d.ts.map +1 -0
- package/dist/src/http.js +150 -0
- package/dist/src/http.js.map +1 -0
- package/dist/src/me.d.ts +53 -0
- package/dist/src/me.d.ts.map +1 -0
- package/dist/src/me.js +77 -0
- package/dist/src/me.js.map +1 -0
- package/dist/src/run.d.ts +92 -0
- package/dist/src/run.d.ts.map +1 -0
- package/dist/src/run.js +83 -0
- package/dist/src/run.js.map +1 -0
- package/dist/src/types.d.ts +362 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +109 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/vars.d.ts +149 -0
- package/dist/src/vars.d.ts.map +1 -0
- package/dist/src/vars.js +149 -0
- package/dist/src/vars.js.map +1 -0
- package/dist/src/version.d.ts +14 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/src/version.js +14 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/workflows.d.ts +180 -0
- package/dist/src/workflows.d.ts.map +1 -0
- package/dist/src/workflows.js +160 -0
- package/dist/src/workflows.js.map +1 -0
- 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"}
|
package/dist/src/env.js
ADDED
|
@@ -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"}
|