@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,151 @@
1
+ /**
2
+ * Configuration resolution: what server, what credential, what project.
3
+ *
4
+ * This module turns whatever the caller supplied — plus the environment, read
5
+ * through the single seam in `src/env.ts` — into one plain {@linkcode
6
+ * ResolvedConfig} value. It is resolved **once, at client construction**, and
7
+ * handed to the transport; nothing downstream re-reads the environment.
8
+ *
9
+ * @module
10
+ */
11
+ /**
12
+ * The API's base path, from the shared contract's `basePath`
13
+ * (`packages/wrappers/endpoints.json`). It is appended to the configured
14
+ * origin by {@linkcode joinBaseUrl}; users never type it.
15
+ */
16
+ export declare const BASE_PATH = "/api";
17
+ /**
18
+ * A `fetch`-shaped function.
19
+ *
20
+ * This is the injection seam every test in this package runs against: no test
21
+ * may require a live server (`docs/implementation.md` §9). It is
22
+ * constructor-injected for the same reason the credential is — a module-patched
23
+ * global cannot be exercised two different ways in one process.
24
+ *
25
+ * `globalThis.fetch` satisfies it (a wider parameter type is assignable to a
26
+ * narrower one), while a fake only has to accept a URL string.
27
+ */
28
+ export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
29
+ /** Constructor options for a client. Every field is optional. */
30
+ export interface W6wClientOptions {
31
+ /**
32
+ * The **origin** of the w6w server, e.g. `https://api.example.com`. The base
33
+ * path (`/api`) is appended for you and is never doubled. It must be an
34
+ * absolute `http(s)` URL with a host: a relative value like `/api` is a
35
+ * browser same-origin assumption with no meaning in a library, and is
36
+ * rejected at construction rather than misreported as an outage later.
37
+ * Overrides `W6W_BASE_URL`.
38
+ */
39
+ baseUrl?: string;
40
+ /** Bearer token, sent on every request. Overrides `W6W_TOKEN`. */
41
+ token?: string;
42
+ /**
43
+ * Default project id for the project-scoped operations — `documents.*` and
44
+ * `workflows.list`, which read it too.
45
+ * Omitted, the server resolves the account's default project. There is no
46
+ * environment variable for this and no `project` on any `vars.*` operation —
47
+ * vars are not project-scoped (`docs/implementation.md` §7).
48
+ */
49
+ project?: string;
50
+ /** Transport override. Defaults to `globalThis.fetch`. */
51
+ fetch?: FetchLike;
52
+ }
53
+ /**
54
+ * Configuration after resolution — the value the transport actually runs on.
55
+ *
56
+ * Held as **instance** state on the client, never in a module-level variable:
57
+ * two clients in one process must be able to hold different credentials and
58
+ * point at different servers with no interference (`docs/implementation.md` §2).
59
+ */
60
+ export interface ResolvedConfig {
61
+ /**
62
+ * Fully joined base, e.g. `https://api.example.com/api`. Never
63
+ * trailing-slashed, and never relative — {@linkcode joinBaseUrl} rejects a
64
+ * value that is not an absolute `http(s)` URL with a host.
65
+ */
66
+ readonly baseUrl: string;
67
+ /** The bearer token, or `null` when none was configured. */
68
+ readonly token: string | null;
69
+ /** Default project id, or `null`. */
70
+ readonly project: string | null;
71
+ }
72
+ /**
73
+ * Validate a configured origin and join it with the API base path.
74
+ *
75
+ * **This is the one and only base-URL code path.** {@linkcode resolveConfig}
76
+ * calls this function and normalises nothing itself, so a caller who imports
77
+ * the helper gets exactly the answer their client will use — including the same
78
+ * errors. A helper that were merely *similar* to the client's path (skipping the
79
+ * whitespace trim, say) would answer differently for the same input, which is
80
+ * worse than having no helper at all. The python lane found and fixed the
81
+ * identical divergence in its own draft; this mirrors its resolution.
82
+ *
83
+ * The join rule is pinned by `docs/implementation.md` §2 and mirrors the house
84
+ * helper that the operator console already uses against this same API, so all
85
+ * clients agree on what `W6W_BASE_URL` means:
86
+ *
87
+ * 1. strip surrounding whitespace, then **all** trailing slashes;
88
+ * 2. reject anything that is not an absolute `http`/`https` URL with a host;
89
+ * 3. if the result already ends with the base path, use it as-is — **never
90
+ * double it**;
91
+ * 4. otherwise append the base path.
92
+ *
93
+ * ```
94
+ * https://api.example.com → https://api.example.com/api
95
+ * https://api.example.com/ → https://api.example.com/api
96
+ * https://api.example.com/// → https://api.example.com/api
97
+ * https://api.example.com/api → https://api.example.com/api
98
+ * https://api.example.com/api/ → https://api.example.com/api
99
+ * ```
100
+ *
101
+ * Step 2 exists because a **relative** base URL has to fail *here*, as
102
+ * configuration, with a message about configuration. Blank-is-absent
103
+ * (`src/env.ts`) already stops `""` from joining to the relative `"/api"`; with
104
+ * no absoluteness check, `W6W_BASE_URL=/foo` walked straight past that guard to
105
+ * `"/foo/api"` and then failed on the first request as "the server may be down"
106
+ * — the same hole, reopened by another route, and misdiagnosed. Closing only one
107
+ * of the two leaves the other open.
108
+ *
109
+ * @param origin - The configured origin. Surrounding whitespace and trailing slashes are tolerated.
110
+ * @returns The joined base URL.
111
+ * @throws {ConfigError} When the origin is blank, relative, hostless or not `http(s)`.
112
+ */
113
+ export declare function joinBaseUrl(origin: string): string;
114
+ /**
115
+ * Resolve constructor options and the environment into one config value.
116
+ *
117
+ * Precedence is **explicit argument > environment variable**, always; there is
118
+ * no default base URL. An explicitly passed empty string is an *explicit*
119
+ * value, not "unset" — it does not fall through to the environment, and an
120
+ * empty base URL is a configuration error either way. Only an absent
121
+ * (`undefined`) argument consults the environment.
122
+ *
123
+ * An **empty or whitespace-only environment variable is absent** and falls
124
+ * through (see `src/env.ts`), so `W6W_BASE_URL=`, `W6W_BASE_URL=" "` and an
125
+ * unset `W6W_BASE_URL` all end here, in the same configuration error — never in
126
+ * a relative `"/api"`.
127
+ *
128
+ * @param options - Constructor options; `fetch` is ignored here (the client owns it).
129
+ * @returns The resolved configuration.
130
+ * @throws {ConfigError} When no usable base URL was supplied, naming `W6W_BASE_URL`.
131
+ */
132
+ export declare function resolveConfig(options?: W6wClientOptions): ResolvedConfig;
133
+ /**
134
+ * The token, or a {@linkcode ConfigError} naming `W6W_TOKEN`.
135
+ *
136
+ * A client may be *constructed* without a token so that a CLI's `--help` and
137
+ * `--version` work offline; the error surfaces on the first request instead.
138
+ * There are no anonymous operations in this surface
139
+ * (`docs/implementation.md` §2).
140
+ *
141
+ * A blank token counts as no token, for the same reason a blank base URL does:
142
+ * `W6W_TOKEN=` is how a shell or a Dockerfile spells "I meant to set this and
143
+ * did not", and `Authorization: Bearer ` would turn that into an opaque 401
144
+ * instead of the one message that explains it.
145
+ *
146
+ * @param config - The resolved configuration.
147
+ * @returns The configured token, verbatim.
148
+ * @throws {ConfigError} When no non-blank token is configured.
149
+ */
150
+ export declare function requireToken(config: ResolvedConfig): string;
151
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH;;;;GAIG;AACH,eAAO,MAAM,SAAS,SAAS,CAAC;AAEhC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjF,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAeD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAuBlD;AAkBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,gBAAqB,GAAG,cAAc,CAS5E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAS3D"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Configuration resolution: what server, what credential, what project.
3
+ *
4
+ * This module turns whatever the caller supplied — plus the environment, read
5
+ * through the single seam in `src/env.ts` — into one plain {@linkcode
6
+ * ResolvedConfig} value. It is resolved **once, at client construction**, and
7
+ * handed to the transport; nothing downstream re-reads the environment.
8
+ *
9
+ * @module
10
+ */
11
+ import { ENV_BASE_URL, ENV_TOKEN, readEnv } from "./env.js";
12
+ import { ConfigError } from "./errors.js";
13
+ /**
14
+ * The API's base path, from the shared contract's `basePath`
15
+ * (`packages/wrappers/endpoints.json`). It is appended to the configured
16
+ * origin by {@linkcode joinBaseUrl}; users never type it.
17
+ */
18
+ export const BASE_PATH = "/api";
19
+ /**
20
+ * An absolute `http(s)` origin, spelled with a real authority.
21
+ *
22
+ * Deliberately checked *before* `new URL`, because the WHATWG parser silently
23
+ * repairs the one spelling this rule most needs to catch: `new URL("http:///foo")`
24
+ * does **not** fail and does **not** yield an empty host — it collapses the extra
25
+ * slash and resolves to `http://foo/`, so a host check alone would accept a
26
+ * hostless value and then request a different server than the one the user typed.
27
+ * Measured in the api container, not assumed. The pattern therefore requires the
28
+ * authority to start with a character that is not a slash, backslash, `?` or `#`.
29
+ */
30
+ const ABSOLUTE_HTTP_ORIGIN = /^https?:\/\/[^/\\?#]/i;
31
+ /**
32
+ * Validate a configured origin and join it with the API base path.
33
+ *
34
+ * **This is the one and only base-URL code path.** {@linkcode resolveConfig}
35
+ * calls this function and normalises nothing itself, so a caller who imports
36
+ * the helper gets exactly the answer their client will use — including the same
37
+ * errors. A helper that were merely *similar* to the client's path (skipping the
38
+ * whitespace trim, say) would answer differently for the same input, which is
39
+ * worse than having no helper at all. The python lane found and fixed the
40
+ * identical divergence in its own draft; this mirrors its resolution.
41
+ *
42
+ * The join rule is pinned by `docs/implementation.md` §2 and mirrors the house
43
+ * helper that the operator console already uses against this same API, so all
44
+ * clients agree on what `W6W_BASE_URL` means:
45
+ *
46
+ * 1. strip surrounding whitespace, then **all** trailing slashes;
47
+ * 2. reject anything that is not an absolute `http`/`https` URL with a host;
48
+ * 3. if the result already ends with the base path, use it as-is — **never
49
+ * double it**;
50
+ * 4. otherwise append the base path.
51
+ *
52
+ * ```
53
+ * https://api.example.com → https://api.example.com/api
54
+ * https://api.example.com/ → https://api.example.com/api
55
+ * https://api.example.com/// → https://api.example.com/api
56
+ * https://api.example.com/api → https://api.example.com/api
57
+ * https://api.example.com/api/ → https://api.example.com/api
58
+ * ```
59
+ *
60
+ * Step 2 exists because a **relative** base URL has to fail *here*, as
61
+ * configuration, with a message about configuration. Blank-is-absent
62
+ * (`src/env.ts`) already stops `""` from joining to the relative `"/api"`; with
63
+ * no absoluteness check, `W6W_BASE_URL=/foo` walked straight past that guard to
64
+ * `"/foo/api"` and then failed on the first request as "the server may be down"
65
+ * — the same hole, reopened by another route, and misdiagnosed. Closing only one
66
+ * of the two leaves the other open.
67
+ *
68
+ * @param origin - The configured origin. Surrounding whitespace and trailing slashes are tolerated.
69
+ * @returns The joined base URL.
70
+ * @throws {ConfigError} When the origin is blank, relative, hostless or not `http(s)`.
71
+ */
72
+ export function joinBaseUrl(origin) {
73
+ // Surrounding whitespace is stripped before the emptiness test so a blank
74
+ // explicit argument fails the same way a blank environment variable does — an
75
+ // env file's stray "\n" must not become part of the origin.
76
+ const trimmed = origin.trim().replace(/\/+$/, "");
77
+ if (trimmed.length === 0) {
78
+ throw new ConfigError("No w6w base URL is configured. Pass one to the client " +
79
+ '(new W6wClient({ baseUrl: "https://api.example.com" })) or set the ' +
80
+ `${ENV_BASE_URL} environment variable. It holds the server's origin — the ` +
81
+ `${BASE_PATH} base path is appended for you.`);
82
+ }
83
+ if (!ABSOLUTE_HTTP_ORIGIN.test(trimmed) || !parses(trimmed)) {
84
+ throw new ConfigError(`The w6w base URL "${trimmed}" is not an absolute http(s) URL. ` +
85
+ `${ENV_BASE_URL} holds an ORIGIN, e.g. "https://api.example.com" — scheme and host, ` +
86
+ `with the ${BASE_PATH} base path appended for you. A relative or hostless value ` +
87
+ "cannot be requested and would fail later as a connection problem rather than as " +
88
+ "the configuration mistake it is.");
89
+ }
90
+ return trimmed.endsWith(BASE_PATH) ? trimmed : `${trimmed}${BASE_PATH}`;
91
+ }
92
+ /**
93
+ * Does the runtime's URL parser accept this origin at all?
94
+ *
95
+ * The pattern above settles *shape* (scheme, `//`, a real authority); this
96
+ * settles the rest — an out-of-range port, a malformed IPv6 literal, a host the
97
+ * parser rejects. Both have to hold, and neither subsumes the other.
98
+ */
99
+ function parses(origin) {
100
+ try {
101
+ new URL(origin);
102
+ return true;
103
+ }
104
+ catch {
105
+ return false;
106
+ }
107
+ }
108
+ /**
109
+ * Resolve constructor options and the environment into one config value.
110
+ *
111
+ * Precedence is **explicit argument > environment variable**, always; there is
112
+ * no default base URL. An explicitly passed empty string is an *explicit*
113
+ * value, not "unset" — it does not fall through to the environment, and an
114
+ * empty base URL is a configuration error either way. Only an absent
115
+ * (`undefined`) argument consults the environment.
116
+ *
117
+ * An **empty or whitespace-only environment variable is absent** and falls
118
+ * through (see `src/env.ts`), so `W6W_BASE_URL=`, `W6W_BASE_URL=" "` and an
119
+ * unset `W6W_BASE_URL` all end here, in the same configuration error — never in
120
+ * a relative `"/api"`.
121
+ *
122
+ * @param options - Constructor options; `fetch` is ignored here (the client owns it).
123
+ * @returns The resolved configuration.
124
+ * @throws {ConfigError} When no usable base URL was supplied, naming `W6W_BASE_URL`.
125
+ */
126
+ export function resolveConfig(options = {}) {
127
+ return {
128
+ // Every normalisation, the emptiness test and the absoluteness check live in
129
+ // `joinBaseUrl` — one code path, so the exported helper and the client can
130
+ // never answer differently for the same input.
131
+ baseUrl: joinBaseUrl(options.baseUrl ?? readEnv(ENV_BASE_URL) ?? ""),
132
+ token: options.token ?? readEnv(ENV_TOKEN) ?? null,
133
+ project: options.project ?? null,
134
+ };
135
+ }
136
+ /**
137
+ * The token, or a {@linkcode ConfigError} naming `W6W_TOKEN`.
138
+ *
139
+ * A client may be *constructed* without a token so that a CLI's `--help` and
140
+ * `--version` work offline; the error surfaces on the first request instead.
141
+ * There are no anonymous operations in this surface
142
+ * (`docs/implementation.md` §2).
143
+ *
144
+ * A blank token counts as no token, for the same reason a blank base URL does:
145
+ * `W6W_TOKEN=` is how a shell or a Dockerfile spells "I meant to set this and
146
+ * did not", and `Authorization: Bearer ` would turn that into an opaque 401
147
+ * instead of the one message that explains it.
148
+ *
149
+ * @param config - The resolved configuration.
150
+ * @returns The configured token, verbatim.
151
+ * @throws {ConfigError} When no non-blank token is configured.
152
+ */
153
+ export function requireToken(config) {
154
+ if (config.token === null || config.token.trim().length === 0) {
155
+ throw new ConfigError("No w6w API token is configured. Pass one to the client " +
156
+ '(new W6wClient({ token: "…" })) or set the ' +
157
+ `${ENV_TOKEN} environment variable. Every w6w API operation is authenticated.`);
158
+ }
159
+ return config.token;
160
+ }
161
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC;AA4DhC;;;;;;;;;;GAUG;AACH,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,0EAA0E;IAC1E,8EAA8E;IAC9E,4DAA4D;IAC5D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAClD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,WAAW,CACnB,wDAAwD;YACtD,qEAAqE;YACrE,GAAG,YAAY,4DAA4D;YAC3E,GAAG,SAAS,iCAAiC,CAChD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,WAAW,CACnB,qBAAqB,OAAO,oCAAoC;YAC9D,GAAG,YAAY,sEAAsE;YACrF,YAAY,SAAS,4DAA4D;YACjF,kFAAkF;YAClF,kCAAkC,CACrC,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,SAAS,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;;GAMG;AACH,SAAS,MAAM,CAAC,MAAc;IAC5B,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,aAAa,CAAC,UAA4B,EAAE;IAC1D,OAAO;QACL,6EAA6E;QAC7E,2EAA2E;QAC3E,+CAA+C;QAC/C,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACpE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI;QAClD,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;KACjC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,YAAY,CAAC,MAAsB;IACjD,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,WAAW,CACnB,yDAAyD;YACvD,6CAA6C;YAC7C,GAAG,SAAS,kEAAkE,CACjF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `client.connections.list()` — discovery, and nothing more.
3
+ *
4
+ * One operation in v0.1.0. It exists because without it a caller has no way to
5
+ * **discover** a `conn_…` id to pass to `run` (D4): the intake's surface is
6
+ * `run` plus the assets it needs, and an id nobody can enumerate is an id
7
+ * nobody can use.
8
+ *
9
+ * Everything else about connections — creating one, testing one, replacing its
10
+ * secret — is `outOfScope` in `endpoints.json` for this version. That is a
11
+ * deliberate line, not an omission: connecting an app is an interactive,
12
+ * secret-handling flow, and half of one in a v0.1.0 wrapper is worse than none.
13
+ * This namespace is therefore read-only and stays that way until the contract
14
+ * says otherwise.
15
+ *
16
+ * The list the server returns is a **redacted projection** — the stored secret
17
+ * and its refresh timestamp never leave the host — so
18
+ * {@linkcode ConnectionSummary} does not declare them.
19
+ *
20
+ * @module
21
+ */
22
+ import type { HttpResponse, RequestOptions } from "./http.ts";
23
+ import { type ConnectionSummary } from "./types.ts";
24
+ /**
25
+ * The slice of `W6wClient` this namespace needs: the transport, and nothing
26
+ * else. Connections carry no `?project=` — the server scopes them by
27
+ * tenant/account from the credential — so, as with `vars`, this host cannot
28
+ * reach the client's configuration to send a scope even by accident.
29
+ */
30
+ export interface ConnectionsHost {
31
+ /** Perform one request. */
32
+ request<T>(options: RequestOptions): Promise<HttpResponse<T>>;
33
+ }
34
+ /**
35
+ * The `connections` namespace on a `W6wClient`.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * for (const c of await client.connections.list()) {
40
+ * if (c.state === "connected") console.log(c.id, c.appId);
41
+ * }
42
+ * ```
43
+ */
44
+ export declare class ConnectionsApi {
45
+ #private;
46
+ /**
47
+ * @param host - The client this namespace issues requests through.
48
+ */
49
+ constructor(host: ConnectionsHost);
50
+ /**
51
+ * List the caller's connections.
52
+ *
53
+ * @returns The connections, unwrapped from the `connections` envelope.
54
+ * @throws {ApiError} On any non-2xx.
55
+ */
56
+ list(): Promise<ConnectionSummary[]>;
57
+ }
58
+ //# sourceMappingURL=connections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/connections.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,KAAK,iBAAiB,EAAU,MAAM,YAAY,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/D;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAc;;IAGzB;;OAEG;gBACS,IAAI,EAAE,eAAe;IAIjC;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;CAI3C"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `client.connections.list()` — discovery, and nothing more.
3
+ *
4
+ * One operation in v0.1.0. It exists because without it a caller has no way to
5
+ * **discover** a `conn_…` id to pass to `run` (D4): the intake's surface is
6
+ * `run` plus the assets it needs, and an id nobody can enumerate is an id
7
+ * nobody can use.
8
+ *
9
+ * Everything else about connections — creating one, testing one, replacing its
10
+ * secret — is `outOfScope` in `endpoints.json` for this version. That is a
11
+ * deliberate line, not an omission: connecting an app is an interactive,
12
+ * secret-handling flow, and half of one in a v0.1.0 wrapper is worse than none.
13
+ * This namespace is therefore read-only and stays that way until the contract
14
+ * says otherwise.
15
+ *
16
+ * The list the server returns is a **redacted projection** — the stored secret
17
+ * and its refresh timestamp never leave the host — so
18
+ * {@linkcode ConnectionSummary} does not declare them.
19
+ *
20
+ * @module
21
+ */
22
+ import { unwrap } from "./types.js";
23
+ /**
24
+ * The `connections` namespace on a `W6wClient`.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * for (const c of await client.connections.list()) {
29
+ * if (c.state === "connected") console.log(c.id, c.appId);
30
+ * }
31
+ * ```
32
+ */
33
+ export class ConnectionsApi {
34
+ #host;
35
+ /**
36
+ * @param host - The client this namespace issues requests through.
37
+ */
38
+ constructor(host) {
39
+ this.#host = host;
40
+ }
41
+ /**
42
+ * List the caller's connections.
43
+ *
44
+ * @returns The connections, unwrapped from the `connections` envelope.
45
+ * @throws {ApiError} On any non-2xx.
46
+ */
47
+ async list() {
48
+ const res = await this.#host.request({ method: "GET", path: "/connections" });
49
+ return unwrap(res, "connections");
50
+ }
51
+ }
52
+ //# sourceMappingURL=connections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.js","sourceRoot":"","sources":["../../src/connections.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAA0B,MAAM,EAAE,MAAM,YAAY,CAAC;AAa5D;;;;;;;;;GASG;AACH,MAAM,OAAO,cAAc;IAChB,KAAK,CAAkB;IAEhC;;OAEG;IACH,YAAY,IAAqB;QAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,IAAI;QACR,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAU,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QACvF,OAAO,MAAM,CAAsB,GAAG,EAAE,aAAa,CAAC,CAAC;IACzD,CAAC;CACF"}
@@ -0,0 +1,168 @@
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 type { ResolvedConfig } from "./config.ts";
24
+ import { type HttpResponse, type RequestOptions } from "./http.ts";
25
+ import { type Doc, type DocFormat } from "./types.ts";
26
+ /**
27
+ * The slice of `W6wClient` this namespace needs: the transport, plus
28
+ * the resolved configuration it reads the **default project** out of. Structural
29
+ * rather than a concrete client type, so the namespace stays independently
30
+ * constructible in a test and this module never imports the client back.
31
+ */
32
+ export interface DocumentsHost {
33
+ /** The resolved configuration; only `project` is read. */
34
+ readonly config: ResolvedConfig;
35
+ /** Perform one request. */
36
+ request<T>(options: RequestOptions): Promise<HttpResponse<T>>;
37
+ }
38
+ /**
39
+ * Per-call options shared by every document operation.
40
+ *
41
+ * `project` overrides the client's default for this one call. Omitted
42
+ * everywhere, the server resolves the account's default project; an unknown id
43
+ * is `400 unknown_project`.
44
+ */
45
+ export interface DocumentOptions {
46
+ /** Project id to scope this call to. */
47
+ project?: string;
48
+ }
49
+ /**
50
+ * The body of `documents.create`.
51
+ *
52
+ * Addressed by `key`, because that is how the server addresses a create (D6).
53
+ */
54
+ export interface DocumentCreateInput {
55
+ /** Non-empty, ≤128 characters, unique per scope + project. `409 document_exists` if taken. */
56
+ key: string;
57
+ /** Raw text; stored verbatim and never parsed. */
58
+ content: string;
59
+ /** Format hint; the server defaults it to `"text"`. */
60
+ format?: DocFormat;
61
+ /** Free text. */
62
+ description?: string;
63
+ }
64
+ /**
65
+ * The body of `documents.update`. Every field is optional; an omitted field is
66
+ * left alone.
67
+ *
68
+ * **`key` is deliberately absent.** The server's PATCH body has no `key` and the
69
+ * key is immutable, so there is no field here to put one in — the type is what
70
+ * enforces it, not a comment.
71
+ */
72
+ export interface DocumentPatch {
73
+ /** Replacement content. */
74
+ content?: string;
75
+ /** Replacement format hint. */
76
+ format?: DocFormat;
77
+ /** Replacement description. */
78
+ description?: string;
79
+ }
80
+ /**
81
+ * The `documents` namespace on a `W6wClient`.
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * const doc = await client.documents.create({ key: "welcome", content: "# Hi" });
86
+ * await client.documents.update(doc.id, { content: "# Hello" });
87
+ * await client.documents.delete(doc.id);
88
+ * ```
89
+ */
90
+ export declare class DocumentsApi {
91
+ #private;
92
+ /**
93
+ * @param host - The client this namespace issues requests through.
94
+ */
95
+ constructor(host: DocumentsHost);
96
+ /**
97
+ * List the caller's documents.
98
+ *
99
+ * @param options - Optional per-call project scope.
100
+ * @returns The documents, unwrapped from the `documents` envelope.
101
+ * @throws {ApiError} On any non-2xx, e.g. `400 unknown_project`.
102
+ */
103
+ list(options?: DocumentOptions): Promise<Doc[]>;
104
+ /**
105
+ * Fetch one document by its server-issued id.
106
+ *
107
+ * @param id - The `doc_…` id.
108
+ * @param options - Optional per-call project scope.
109
+ * @returns The document.
110
+ * @throws {ApiError} `404 unknown_document` when there is no such id.
111
+ */
112
+ get(id: string, options?: DocumentOptions): Promise<Doc>;
113
+ /**
114
+ * Fetch one document by the `key` its author chose.
115
+ *
116
+ * The key is percent-encoded at interpolation by the `path` tag, which is not
117
+ * ceremony: the server accepts any non-empty key up to 128 characters, so
118
+ * `"a/b"` and `".."` are keys a user can legitimately create, and concatenated
119
+ * naively they address a *different route* — `".."` resolves to the list
120
+ * endpoint and returns every document with a `200` and no error anywhere.
121
+ *
122
+ * Encoding, never validation: a key the server accepts is sent as-is
123
+ * (encoded). This method does not reject, trim or canonicalise a key —
124
+ * refusing one locally would make a document that exists permanently
125
+ * unreadable through this client.
126
+ *
127
+ * @param key - The document key, sent encoded and otherwise untouched.
128
+ * @param options - Optional per-call project scope.
129
+ * @returns The document.
130
+ * @throws {ApiError} `404 unknown_document` when there is no such key.
131
+ */
132
+ getByKey(key: string, options?: DocumentOptions): Promise<Doc>;
133
+ /**
134
+ * Create a document under a caller-chosen key.
135
+ *
136
+ * @param input - `key` and `content` are required; `format` defaults server-side to `"text"`.
137
+ * @param options - Optional per-call project scope.
138
+ * @returns The created document (the server answers `201`).
139
+ * @throws {ApiError} `409 document_exists` on a duplicate key — the code reaches the caller intact.
140
+ */
141
+ create(input: DocumentCreateInput, options?: DocumentOptions): Promise<Doc>;
142
+ /**
143
+ * Patch a document's content, format or description.
144
+ *
145
+ * Addressed by id, never by key, and the key itself cannot be changed.
146
+ *
147
+ * @param id - The `doc_…` id.
148
+ * @param patch - The fields to change; omitted fields are left alone.
149
+ * @param options - Optional per-call project scope.
150
+ * @returns The updated document.
151
+ * @throws {ApiError} `404 unknown_document` when there is no such id.
152
+ */
153
+ update(id: string, patch: DocumentPatch, options?: DocumentOptions): Promise<Doc>;
154
+ /**
155
+ * Delete a document.
156
+ *
157
+ * Returns nothing: the server's `{ok: true}` carries no information a caller
158
+ * can use, and unwrapping it to a value would invite `if (await delete(...))`.
159
+ * Deleting an unknown id is `404 unknown_document`, **not** a silent success —
160
+ * the delete is not idempotent and this method does not pretend otherwise.
161
+ *
162
+ * @param id - The `doc_…` id.
163
+ * @param options - Optional per-call project scope.
164
+ * @throws {ApiError} `404 unknown_document` when there is no such id.
165
+ */
166
+ delete(id: string, options?: DocumentOptions): Promise<void>;
167
+ }
168
+ //# sourceMappingURL=documents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/documents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,KAAK,YAAY,EAAQ,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,SAAS,EAAU,MAAM,YAAY,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,2BAA2B;IAC3B,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,8FAA8F;IAC9F,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,iBAAiB;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,qBAAa,YAAY;;IAGvB;;OAEG;gBACS,IAAI,EAAE,aAAa;IAa/B;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IASrD;;;;;;;OAOG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;IAS9D;;;;;;;;;;;;;;;;;;OAkBG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;IASpE;;;;;;;OAOG;IACG,MAAM,CAAC,KAAK,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;IAkBjF;;;;;;;;;;OAUG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;IAcvF;;;;;;;;;;;OAWG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;CAOnE"}