@k9o/core 1.0.0 → 1.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/CHANGELOG.md +5 -71
- package/README.md +224 -32
- package/dist/{chunk-OE2MMUIG.js → chunk-2NDFTHUB.js} +2 -2
- package/dist/chunk-2NDFTHUB.js.map +1 -0
- package/dist/{chunk-2VSTF5UU.js → chunk-LJZ45UA2.js} +1 -1
- package/dist/chunk-LJZ45UA2.js.map +1 -0
- package/dist/{chunk-FVWKL5UQ.js → chunk-O2ZTV6YN.js} +2 -2
- package/dist/chunk-O2ZTV6YN.js.map +1 -0
- package/dist/envelope.d.ts +9 -9
- package/dist/http.d.ts +4 -4
- package/dist/http.js +2 -2
- package/dist/index.d.ts +163 -183
- package/dist/index.js +43 -44
- package/dist/index.js.map +1 -1
- package/dist/kerror.d.ts +2 -2
- package/dist/kerror.js +1 -1
- package/dist/types.d.ts +4 -6
- package/dist/url-grammar.d.ts +6 -6
- package/dist/url-grammar.js +2 -2
- package/package.json +4 -4
- package/src/audit.ts +6 -6
- package/src/compute/llms.ts +16 -16
- package/src/data/from.ts +12 -12
- package/src/envelope.ts +9 -9
- package/src/extensions-bridge.ts +7 -9
- package/src/functions.ts +8 -8
- package/src/gate-bridge.ts +2 -2
- package/src/http.ts +4 -4
- package/src/index.ts +17 -27
- package/src/internal/sse.ts +2 -2
- package/src/kerror.ts +2 -2
- package/src/me.ts +7 -8
- package/src/metadata.ts +13 -20
- package/src/migrations.ts +12 -12
- package/src/org.ts +19 -19
- package/src/orgs.ts +5 -5
- package/src/policies.ts +2 -2
- package/src/projects.ts +5 -5
- package/src/realtime.ts +16 -16
- package/src/retry.ts +1 -1
- package/src/storage-buckets.ts +1 -1
- package/src/studio/dashboards.ts +13 -13
- package/src/studio/index.ts +2 -2
- package/src/studio/workflows.ts +21 -21
- package/src/types.ts +4 -6
- package/src/url-grammar.ts +6 -6
- package/dist/chunk-2VSTF5UU.js.map +0 -1
- package/dist/chunk-FVWKL5UQ.js.map +0 -1
- package/dist/chunk-OE2MMUIG.js.map +0 -1
package/src/realtime.ts
CHANGED
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
* `for await (const event of client.realtime.subscribe(topic)) { ... }`.
|
|
9
9
|
*
|
|
10
10
|
* Per `feedback_sdk_abstracted_primitive_default_to_richer_interaction.md`
|
|
11
|
-
*
|
|
11
|
+
* +: the SDK chooses the transport (WebSocket primary → SSE
|
|
12
12
|
* → poll), transparently. SPLIT (polling) is the SDK-internal fallback
|
|
13
13
|
* when WebSocket / SSE are unavailable — NOT a consumer-side design
|
|
14
14
|
* choice. The consumer sees one uniform async iterable regardless.
|
|
15
15
|
*
|
|
16
|
-
* Wire mapping (project-plane;
|
|
16
|
+
* Wire mapping (project-plane;):
|
|
17
17
|
* - subscribe → GET /@<slug>/realtime/subscribe?topic=<t>&filter=<jsonb> (WS/SSE upgrade)
|
|
18
18
|
* - replay → POST /@<slug>/realtime/replay { topic, fromSequence, untilSequence?, limit? }
|
|
19
19
|
*
|
|
20
|
-
* Per
|
|
21
|
-
* Per
|
|
20
|
+
* Per, `filter` + RLS compose (filter narrows, RLS gates).
|
|
21
|
+
* Per, surfaces no new codes — reuses K9O_INVALID_FILTER +
|
|
22
22
|
* K9O_SUBSCRIPTION_LIMIT_EXCEEDED + K9O_TIER_FEATURE_GATED.
|
|
23
23
|
*/
|
|
24
24
|
|
|
@@ -29,11 +29,11 @@ import type { Envelope } from "./envelope.ts";
|
|
|
29
29
|
import { parseSseStream } from "./internal/sse.ts";
|
|
30
30
|
|
|
31
31
|
// ----------------------------------------------------------------------
|
|
32
|
-
// Topic catalogue (closed 6-family enum
|
|
32
|
+
// Topic catalogue (closed 6-family enum)
|
|
33
33
|
// ----------------------------------------------------------------------
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* The 6 closed subscription topic families
|
|
36
|
+
* The 6 closed subscription topic families. Concrete
|
|
37
37
|
* topic strings carry a dynamic segment (collection name, resource id,
|
|
38
38
|
* event family) — use `realtimeTopic.*` builders to construct them.
|
|
39
39
|
*/
|
|
@@ -49,7 +49,7 @@ export type RealtimeTopicFamily =
|
|
|
49
49
|
export type RealtimeTopic = string;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* Typed builders for the 6 topic families
|
|
52
|
+
* Typed builders for the 6 topic families. Prefer these
|
|
53
53
|
* over hand-built strings for compile-time family safety.
|
|
54
54
|
*/
|
|
55
55
|
export const realtimeTopic = {
|
|
@@ -83,10 +83,10 @@ export const realtimeTopic = {
|
|
|
83
83
|
// Event + filter shapes
|
|
84
84
|
// ----------------------------------------------------------------------
|
|
85
85
|
|
|
86
|
-
/** Per-topic filter predicate
|
|
86
|
+
/** Per-topic filter predicate (vocabulary). */
|
|
87
87
|
export type RealtimeFilter = Record<string, unknown>;
|
|
88
88
|
|
|
89
|
-
/** A single realtime event delivered on a subscription
|
|
89
|
+
/** A single realtime event delivered on a subscription. */
|
|
90
90
|
export type RealtimeEvent<T = unknown> = {
|
|
91
91
|
topic: RealtimeTopic;
|
|
92
92
|
/** Event type within the topic family (e.g. `row_inserted`, `workflow.run.completed`). */
|
|
@@ -104,7 +104,7 @@ export type SubscribeOptions = {
|
|
|
104
104
|
/**
|
|
105
105
|
* Force a transport instead of the WebSocket → SSE → poll cascade.
|
|
106
106
|
* Mainly for tests + constrained runtimes. Omit for production —
|
|
107
|
-
* the SDK picks the richest available transport
|
|
107
|
+
* the SDK picks the richest available transport.
|
|
108
108
|
*/
|
|
109
109
|
transport?: RealtimeTransportKind;
|
|
110
110
|
/** Poll-transport interval in ms (default 2000). Only used when polling. */
|
|
@@ -139,7 +139,7 @@ function requireProjectSlug(config: HttpClientConfig): string {
|
|
|
139
139
|
return config.projectSlug;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
/** Pick the richest transport available in this runtime
|
|
142
|
+
/** Pick the richest transport available in this runtime. */
|
|
143
143
|
function selectTransport(forced?: RealtimeTransportKind): RealtimeTransportKind {
|
|
144
144
|
if (forced) return forced;
|
|
145
145
|
if (typeof (globalThis as { WebSocket?: unknown }).WebSocket === "function") return "websocket";
|
|
@@ -155,7 +155,7 @@ function toWebSocketUrl(baseUrl: string, path: string, query: Record<string, str
|
|
|
155
155
|
return `${wsBase}${path}${qs ? `?${qs}` : ""}`;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
/** WebSocket transport — primary
|
|
158
|
+
/** WebSocket transport — primary. Bridges socket → async queue. */
|
|
159
159
|
async function* websocketTransport<T>(
|
|
160
160
|
config: HttpClientConfig,
|
|
161
161
|
topic: RealtimeTopic,
|
|
@@ -222,7 +222,7 @@ async function* websocketTransport<T>(
|
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
/** SSE transport — fallback
|
|
225
|
+
/** SSE transport — fallback. Rides on fetch streaming. */
|
|
226
226
|
async function* sseTransport<T>(
|
|
227
227
|
config: HttpClientConfig,
|
|
228
228
|
topic: RealtimeTopic,
|
|
@@ -244,7 +244,7 @@ async function* sseTransport<T>(
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
/** Poll transport — last-resort fallback
|
|
247
|
+
/** Poll transport — last-resort fallback (SPLIT, SDK-internal). */
|
|
248
248
|
async function* pollTransport<T>(
|
|
249
249
|
config: HttpClientConfig,
|
|
250
250
|
topic: RealtimeTopic,
|
|
@@ -275,7 +275,7 @@ export function makeRealtimeNamespace(config: HttpClientConfig) {
|
|
|
275
275
|
/**
|
|
276
276
|
* Subscribe to a topic. Returns a `RealtimeSubscription` — an async
|
|
277
277
|
* iterable of `RealtimeEvent`s. The SDK selects the richest available
|
|
278
|
-
* transport (WebSocket → SSE → poll)
|
|
278
|
+
* transport (WebSocket → SSE → poll); the caller's
|
|
279
279
|
* `for await` loop is transport-agnostic.
|
|
280
280
|
*
|
|
281
281
|
* ```ts
|
|
@@ -319,7 +319,7 @@ export function makeRealtimeNamespace(config: HttpClientConfig) {
|
|
|
319
319
|
},
|
|
320
320
|
|
|
321
321
|
/**
|
|
322
|
-
* One-shot historical replay
|
|
322
|
+
* One-shot historical replay — catch up on missed
|
|
323
323
|
* events from `fromSequence`. Capped at 10k events server-side;
|
|
324
324
|
* paginate via the returned events' `sequence`. Use the highest
|
|
325
325
|
* `sequence` you've seen as the next `fromSequence`.
|
package/src/retry.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* Retry method discipline: only GET / HEAD / OPTIONS / DELETE are
|
|
20
20
|
* retried by default — these are RFC-7231 idempotent. POST / PATCH /
|
|
21
21
|
* PUT are NOT retried (caller can opt in via `idempotencyKey` header
|
|
22
|
-
*
|
|
22
|
+
* when they assert idempotency).
|
|
23
23
|
*
|
|
24
24
|
* Backoff:
|
|
25
25
|
* - `exponential` (default) — `initialDelayMs * 2^attempt`, capped at maxDelayMs; jittered ±25%
|
package/src/storage-buckets.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `client.storage` buckets and objects
|
|
2
|
+
* `client.storage` buckets and objects: per-project logical buckets and the objects in them, against
|
|
3
3
|
* `/@<projectSlug>/storage/buckets…`. `client.storage.locations.*` (the customer's own object stores) lives in
|
|
4
4
|
* `storage-locations.ts` and is unchanged.
|
|
5
5
|
*
|
package/src/studio/dashboards.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Studio Dashboards SDK — `client.studio.dashboards.*`.
|
|
3
3
|
*
|
|
4
4
|
* Dashboards persisted authoring substrate.
|
|
5
|
-
* Mirrors `_sys.dashboards`
|
|
5
|
+
* Mirrors `_sys.dashboards` row shape.
|
|
6
6
|
*
|
|
7
|
-
* Wire mapping (project-plane;
|
|
7
|
+
* Wire mapping (project-plane;):
|
|
8
8
|
* - list() → GET /@<slug>/dashboards
|
|
9
9
|
* - create(dashboard) → POST /@<slug>/dashboards
|
|
10
10
|
* - read(id) → GET /@<slug>/dashboards/<dsh-id>
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
* - pin(id) → POST /@<slug>/dashboards/<dsh-id>/pin
|
|
14
14
|
* - unpin(id) → POST /@<slug>/dashboards/<dsh-id>/unpin
|
|
15
15
|
*
|
|
16
|
-
* Per
|
|
16
|
+
* Per RBAC: create/update/delete/pin/unpin require
|
|
17
17
|
* PROJECT_AUTHOR; list/read read against all cohort roles subject
|
|
18
|
-
* to RLS. Per
|
|
18
|
+
* to RLS. Per, surfaces 2 K9O_DASHBOARD_* codes.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { request, type HttpClientConfig } from "../http.ts";
|
|
@@ -23,10 +23,10 @@ import { projectPath } from "../url-grammar.ts";
|
|
|
23
23
|
import type { Envelope } from "../envelope.ts";
|
|
24
24
|
|
|
25
25
|
// ----------------------------------------------------------------------
|
|
26
|
-
// Enums (closed at v2 launch
|
|
26
|
+
// Enums (closed at v2 launch +)
|
|
27
27
|
// ----------------------------------------------------------------------
|
|
28
28
|
|
|
29
|
-
/** 9-widget catalogue
|
|
29
|
+
/** 9-widget catalogue. */
|
|
30
30
|
export type WidgetType =
|
|
31
31
|
| "kpi_card"
|
|
32
32
|
| "bar_chart"
|
|
@@ -43,7 +43,7 @@ export type WidgetDataSourceMode = "view" | "query" | "workflow" | "rollup" | "f
|
|
|
43
43
|
|
|
44
44
|
export type DashboardStatus = "active" | "archived" | "draft";
|
|
45
45
|
|
|
46
|
-
/** A widget config entry in the `widgets` array
|
|
46
|
+
/** A widget config entry in the `widgets` array. */
|
|
47
47
|
export type Widget = {
|
|
48
48
|
id: string;
|
|
49
49
|
widgetType: WidgetType;
|
|
@@ -61,20 +61,20 @@ export type Widget = {
|
|
|
61
61
|
// Resource shapes (mirror _sys.dashboards row)
|
|
62
62
|
// ----------------------------------------------------------------------
|
|
63
63
|
|
|
64
|
-
/** Dashboard record
|
|
64
|
+
/** Dashboard record `_sys.dashboards` row. */
|
|
65
65
|
export type Dashboard = {
|
|
66
|
-
/** `dsh_<base62
|
|
66
|
+
/** `dsh_<base62>`. */
|
|
67
67
|
id: string;
|
|
68
68
|
projectId: string;
|
|
69
69
|
name: string;
|
|
70
70
|
description: string;
|
|
71
71
|
status: DashboardStatus;
|
|
72
|
-
/** Widget positions + sizes per output-03
|
|
72
|
+
/** Widget positions + sizes per output-03 layout schema. */
|
|
73
73
|
layoutGrid: Record<string, unknown>;
|
|
74
74
|
widgets: ReadonlyArray<Widget>;
|
|
75
75
|
/** Dashboard-level filter widgets cascade to per-widget data sources. */
|
|
76
76
|
filterConfig: Record<string, unknown>;
|
|
77
|
-
/** Pathmaker org-pin per output-03
|
|
77
|
+
/** Pathmaker org-pin per output-03; surfaces in Workspace sidebar. */
|
|
78
78
|
pinnedToWorkspace: boolean;
|
|
79
79
|
rbacBinding: Record<string, unknown>;
|
|
80
80
|
authoredBy: string;
|
|
@@ -102,7 +102,7 @@ export type DashboardListFilters = {
|
|
|
102
102
|
pinnedToWorkspace?: boolean;
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
-
/** 2 K9O_DASHBOARD_* error codes
|
|
105
|
+
/** 2 K9O_DASHBOARD_* error codes. */
|
|
106
106
|
export const DASHBOARD_ERROR_CODES = {
|
|
107
107
|
NOT_FOUND: "K9O_DASHBOARD_NOT_FOUND",
|
|
108
108
|
WIDGET_INVALID: "K9O_DASHBOARD_WIDGET_INVALID",
|
|
@@ -117,7 +117,7 @@ function requireProjectSlug(config: HttpClientConfig): string {
|
|
|
117
117
|
|
|
118
118
|
export function makeDashboardsNamespace(config: HttpClientConfig) {
|
|
119
119
|
return {
|
|
120
|
-
/** GET /@<slug>/dashboards — list dashboards (cursor pagination
|
|
120
|
+
/** GET /@<slug>/dashboards — list dashboards (cursor pagination). */
|
|
121
121
|
async list(filters?: DashboardListFilters): Promise<Envelope<ReadonlyArray<Dashboard>>> {
|
|
122
122
|
const slug = requireProjectSlug(config);
|
|
123
123
|
return request(config, { method: "GET", path: projectPath(slug, "dashboards"), query: filters });
|
package/src/studio/index.ts
CHANGED
|
@@ -10,9 +10,9 @@ import { makeDashboardsNamespace } from "./dashboards.ts";
|
|
|
10
10
|
|
|
11
11
|
export function makeStudioNamespace(config: HttpClientConfig) {
|
|
12
12
|
return {
|
|
13
|
-
/** Workflow lifecycle + runs — `client.studio.workflows
|
|
13
|
+
/** Workflow lifecycle + runs — `client.studio.workflows.*`. */
|
|
14
14
|
workflows: makeWorkflowsNamespace(config),
|
|
15
|
-
/** Dashboard lifecycle + widget data — `client.studio.dashboards
|
|
15
|
+
/** Dashboard lifecycle + widget data — `client.studio.dashboards.*`. */
|
|
16
16
|
dashboards: makeDashboardsNamespace(config),
|
|
17
17
|
};
|
|
18
18
|
}
|
package/src/studio/workflows.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Studio Workflows SDK — `client.studio.workflows.*`.
|
|
3
3
|
*
|
|
4
4
|
* Workflows substrate. Mirrors
|
|
5
|
-
* `_sys.workflows`
|
|
5
|
+
* `_sys.workflows` + `_sys.workflow_runs` row shapes.
|
|
6
6
|
*
|
|
7
|
-
* Wire mapping (project-plane;
|
|
7
|
+
* Wire mapping (project-plane;):
|
|
8
8
|
* - list() → GET /@<slug>/workflows
|
|
9
9
|
* - create(workflow) → POST /@<slug>/workflows
|
|
10
10
|
* - read(id) → GET /@<slug>/workflows/<wfl-id>
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
* - runs.read(id, runId) → GET /@<slug>/workflows/<wfl-id>/runs/<wfr-id>
|
|
16
16
|
* - runs.cancel(id, runId) → POST /@<slug>/workflows/<wfl-id>/runs/<wfr-id>/cancel
|
|
17
17
|
*
|
|
18
|
-
* Per
|
|
18
|
+
* Per RBAC: create/update/delete/trigger/cancel require
|
|
19
19
|
* PROJECT_AUTHOR; list/read/runs read against all cohort roles subject
|
|
20
|
-
* to RLS. Per
|
|
20
|
+
* to RLS. Per, surfaces 6 K9O_WORKFLOW_* error codes (see
|
|
21
21
|
* `WORKFLOW_ERROR_CODES`); callers narrow on `error.code`.
|
|
22
22
|
*/
|
|
23
23
|
|
|
@@ -26,10 +26,10 @@ import { projectPath } from "../url-grammar.ts";
|
|
|
26
26
|
import type { Envelope } from "../envelope.ts";
|
|
27
27
|
|
|
28
28
|
// ----------------------------------------------------------------------
|
|
29
|
-
// Enums (closed at v2 launch
|
|
29
|
+
// Enums (closed at v2 launch +; extensible v2.x)
|
|
30
30
|
// ----------------------------------------------------------------------
|
|
31
31
|
|
|
32
|
-
/** 5-trigger catalogue
|
|
32
|
+
/** 5-trigger catalogue. */
|
|
33
33
|
export type WorkflowTriggerType =
|
|
34
34
|
| "view_row_event"
|
|
35
35
|
| "form_submission"
|
|
@@ -37,7 +37,7 @@ export type WorkflowTriggerType =
|
|
|
37
37
|
| "manual"
|
|
38
38
|
| "webhook";
|
|
39
39
|
|
|
40
|
-
/** 9-action catalogue
|
|
40
|
+
/** 9-action catalogue. */
|
|
41
41
|
export type WorkflowActionType =
|
|
42
42
|
| "write_collection"
|
|
43
43
|
| "read_collection"
|
|
@@ -58,14 +58,14 @@ export type WorkflowRunStatus =
|
|
|
58
58
|
| "failed"
|
|
59
59
|
| "cancelled";
|
|
60
60
|
|
|
61
|
-
/** A single step in the `actions` chain
|
|
61
|
+
/** A single step in the `actions` chain. */
|
|
62
62
|
export type WorkflowAction = {
|
|
63
63
|
index: number;
|
|
64
64
|
action_type: WorkflowActionType;
|
|
65
65
|
config: Record<string, unknown>;
|
|
66
66
|
retry?: { maxAttempts: number; backoffStrategy?: string };
|
|
67
67
|
condition?: Record<string, unknown>;
|
|
68
|
-
/** Sibling steps with parallel:true execute concurrently
|
|
68
|
+
/** Sibling steps with parallel:true execute concurrently. */
|
|
69
69
|
parallel?: boolean;
|
|
70
70
|
};
|
|
71
71
|
|
|
@@ -73,20 +73,20 @@ export type WorkflowAction = {
|
|
|
73
73
|
// Resource shapes (mirror _sys.workflows + _sys.workflow_runs rows)
|
|
74
74
|
// ----------------------------------------------------------------------
|
|
75
75
|
|
|
76
|
-
/** Workflow record
|
|
76
|
+
/** Workflow record `_sys.workflows` row. */
|
|
77
77
|
export type Workflow = {
|
|
78
|
-
/** `wfl_<base62
|
|
78
|
+
/** `wfl_<base62>`. */
|
|
79
79
|
id: string;
|
|
80
80
|
projectId: string;
|
|
81
81
|
name: string;
|
|
82
82
|
description: string;
|
|
83
83
|
status: WorkflowStatus;
|
|
84
84
|
triggerType: WorkflowTriggerType;
|
|
85
|
-
/** Shape varies per triggerType
|
|
85
|
+
/** Shape varies per triggerType. */
|
|
86
86
|
triggerConfig: Record<string, unknown>;
|
|
87
|
-
/** Chain definition
|
|
87
|
+
/** Chain definition. */
|
|
88
88
|
actions: ReadonlyArray<WorkflowAction>;
|
|
89
|
-
/** Approver-step participant resolution
|
|
89
|
+
/** Approver-step participant resolution. */
|
|
90
90
|
rbacBinding: Record<string, unknown>;
|
|
91
91
|
authoredBy: string;
|
|
92
92
|
authoredAt: string;
|
|
@@ -94,7 +94,7 @@ export type Workflow = {
|
|
|
94
94
|
updatedAt: string;
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
-
/** Per-step status entry in a run
|
|
97
|
+
/** Per-step status entry in a run `per_step_status`. */
|
|
98
98
|
export type WorkflowRunStep = {
|
|
99
99
|
stepIndex: number;
|
|
100
100
|
actionType: WorkflowActionType;
|
|
@@ -106,9 +106,9 @@ export type WorkflowRunStep = {
|
|
|
106
106
|
costSubtotal?: number;
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
/** Workflow run record
|
|
109
|
+
/** Workflow run record `_sys.workflow_runs` row. */
|
|
110
110
|
export type WorkflowRun = {
|
|
111
|
-
/** `wfr_<base62
|
|
111
|
+
/** `wfr_<base62>`. */
|
|
112
112
|
id: string;
|
|
113
113
|
workflowId: string;
|
|
114
114
|
projectId: string;
|
|
@@ -122,7 +122,7 @@ export type WorkflowRun = {
|
|
|
122
122
|
firedBy: string;
|
|
123
123
|
};
|
|
124
124
|
|
|
125
|
-
/** Create input — omits server-assigned fields
|
|
125
|
+
/** Create input — omits server-assigned fields. */
|
|
126
126
|
export type CreateWorkflowInput = {
|
|
127
127
|
name: string;
|
|
128
128
|
description?: string;
|
|
@@ -154,7 +154,7 @@ export type WorkflowRunListFilters = {
|
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
|
-
* 6 K9O_WORKFLOW_* error codes
|
|
157
|
+
* 6 K9O_WORKFLOW_* error codes. KError code-space is
|
|
158
158
|
* open; these constants document the well-known codes for narrowing
|
|
159
159
|
* (`if (e.code === WORKFLOW_ERROR_CODES.RUN_NOT_CANCELLABLE)`).
|
|
160
160
|
*/
|
|
@@ -208,7 +208,7 @@ function makeRunsNamespace(config: HttpClientConfig) {
|
|
|
208
208
|
|
|
209
209
|
export function makeWorkflowsNamespace(config: HttpClientConfig) {
|
|
210
210
|
return {
|
|
211
|
-
/** GET /@<slug>/workflows — list workflows (cursor pagination
|
|
211
|
+
/** GET /@<slug>/workflows — list workflows (cursor pagination). */
|
|
212
212
|
async list(filters?: WorkflowListFilters): Promise<Envelope<ReadonlyArray<Workflow>>> {
|
|
213
213
|
const slug = requireProjectSlug(config);
|
|
214
214
|
return request(config, { method: "GET", path: projectPath(slug, "workflows"), query: filters });
|
|
@@ -250,7 +250,7 @@ export function makeWorkflowsNamespace(config: HttpClientConfig) {
|
|
|
250
250
|
|
|
251
251
|
/**
|
|
252
252
|
* POST /@<slug>/workflows/<wfl-id>/runs — manual-trigger a run.
|
|
253
|
-
* Valid only for `triggerType: 'manual'` workflows
|
|
253
|
+
* Valid only for `triggerType: 'manual'` workflows.
|
|
254
254
|
* Requires PROJECT_AUTHOR. Returns the materialised WorkflowRun.
|
|
255
255
|
*/
|
|
256
256
|
async trigger(workflowId: string, triggerContext: Record<string, unknown> = {}): Promise<Envelope<WorkflowRun>> {
|
package/src/types.ts
CHANGED
|
@@ -25,11 +25,10 @@
|
|
|
25
25
|
* - `baseUrl` — gateway host root (scheme + host; no path). **Optional**;
|
|
26
26
|
* defaults to the platform apex `https://api.k9o.io`.
|
|
27
27
|
* When it is the bare apex and `orgSlug` is set, the SDK composes the
|
|
28
|
-
* canonical org subdomain `https://<orgSlug>.api.k9o.io
|
|
29
|
-
* §7.1). Pass an explicit host to override — an explicit `<orgSlug>` subdomain,
|
|
28
|
+
* canonical org subdomain `https://<orgSlug>.api.k9o.io`. Pass an explicit host to override — an explicit `<orgSlug>` subdomain,
|
|
30
29
|
* or a self-hosted / Sovereign-Control host, or a dev host — used as-is.
|
|
31
30
|
* - `fetch` — custom fetch implementation (defaults to global)
|
|
32
|
-
* - `retryConfig` — retry/backoff config (reserved;
|
|
31
|
+
* - `retryConfig` — retry/backoff config (reserved; currently ships single-attempt)
|
|
33
32
|
* - `defaultHeaders` — headers merged into every request
|
|
34
33
|
*/
|
|
35
34
|
export type CreateClientOptions = {
|
|
@@ -43,9 +42,8 @@ export type CreateClientOptions = {
|
|
|
43
42
|
};
|
|
44
43
|
|
|
45
44
|
/**
|
|
46
|
-
* Retry config
|
|
47
|
-
*
|
|
48
|
-
* sub-cycle.
|
|
45
|
+
* Retry config. The type is reserved; the HTTP client currently makes a
|
|
46
|
+
* single attempt per request and does not yet honour this config.
|
|
49
47
|
*/
|
|
50
48
|
export type RetryConfig = {
|
|
51
49
|
maxRetries: number;
|
package/src/url-grammar.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* URL grammar
|
|
2
|
+
* URL grammar.
|
|
3
3
|
*
|
|
4
4
|
* Canonical URL shapes:
|
|
5
5
|
* - Member-plane (project-scoped): `<orgSlug>.api.k9o.io/@<projectSlug>/<sub-resource>/...`
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* - Platform-plane (cross-org): `api.k9o.io/discovery/...` + `api.k9o.io/auth/...` etc.
|
|
8
8
|
*
|
|
9
9
|
* The `baseUrl` passed to `createClient` is the gateway host root —
|
|
10
|
-
* scheme + host (+ optional port)
|
|
11
|
-
* are rejected with `K9O_INVALID_BASE_URL
|
|
10
|
+
* scheme + host (+ optional port). Path-bearing baseUrls
|
|
11
|
+
* are rejected with `K9O_INVALID_BASE_URL`.
|
|
12
12
|
*
|
|
13
13
|
* The SDK constructs URLs by composing `baseUrl + path-from-builder`.
|
|
14
14
|
* Per-resource SDK methods use the builders below to assemble their
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { KError } from "./kerror.ts";
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Validate a `baseUrl
|
|
21
|
+
* Validate a `baseUrl`. Returns the normalised form
|
|
22
22
|
* (trailing-slash stripped) on success; throws `K9O_INVALID_BASE_URL`
|
|
23
23
|
* on any violation.
|
|
24
24
|
*
|
|
@@ -94,7 +94,7 @@ const K9O_ORG_DERIVABLE_APEXES = new Set(["api.k9o.io", "api-staging.k9o.io"]);
|
|
|
94
94
|
* OPTIONAL — it defaults to the platform apex `https://api.k9o.io`. When the
|
|
95
95
|
* (validated) host is a bare apex in `K9O_ORG_DERIVABLE_APEXES` and `orgSlug`
|
|
96
96
|
* is supplied, the SDK composes the canonical org-subdomain wire root
|
|
97
|
-
* `https://<orgSlug>.<apex
|
|
97
|
+
* `https://<orgSlug>.<apex>`. An explicit `<orgSlug>` subdomain, a
|
|
98
98
|
* self-hosted / Sovereign host, or a dev host (anything not in that set)
|
|
99
99
|
* passes through unchanged — `orgSlug` is intentionally NOT validated or
|
|
100
100
|
* composed onto a non-apex host (fixed 2026-08-30: this used to silently
|
|
@@ -131,7 +131,7 @@ export function validateSlug(slug: string, kind: "org" | "project"): void {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* Build a project-scoped (member-plane) URL path per
|
|
134
|
+
* Build a project-scoped (member-plane) URL path per the URL grammar
|
|
135
135
|
* E.g. `projectPath('member-portal', 'collections/posts/documents')`
|
|
136
136
|
* → `/@member-portal/collections/posts/documents`.
|
|
137
137
|
*
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/kerror.ts"],"sourcesContent":["/**\n * `KError` — the closed-taxonomy error class returned by every `@k9o/core`\n * surface on non-2xx gateway responses or SDK-side validation failures.\n *\n * TypeScript / JavaScript throw on error; the per-language SDKs deliver this same payload\n * via their idiomatic mechanism (Go tuple-return; Rust Result; Python\n * raise; etc.) without changing the field shape.\n *\n * Fields:\n * - `code` — closed-taxonomy string (e.g. `K9O_TIER_CAP_HIT`,\n * `K9O_PROJECT_NOT_FOUND`); unknown codes pass through\n * with `code` preserved + `INTERNAL_ERROR` family\n * classification recorded in `context.originalCode`.\n * - `message` — human-readable explanation.\n * - `context` — structured per-code payload (immutable; frozen).\n * - `recommendation` — actionable next step (may be null when the\n * gateway didn't return one).\n * - `status` — HTTP status the gateway returned (or null for\n * SDK-side construct-time errors like\n * `K9O_INVALID_BASE_URL` per ch13 §7.2).\n * - `requestId` — gateway-emitted trace ID for downstream correlation.\n *\n * Standalone `@k9o/gate` ships its own `GateError` for the Auth0-parity\n * deployment shape; when `@k9o/gate` is composed inside `@k9o/core`,\n * `GateError` semantics align with `KError extends KError` (alignment still open).\n * Until then `KError` is the canonical error type returned by `@k9o/core` surfaces; `GateError` thrown by\n * `@k9o/gate`-delegated surfaces remain `KError`-shape-compatible\n * (both expose the same `code` / `message` / `context` / `recommendation`\n * fields) so caller code that narrows on `code` works against either.\n */\n\n/**\n * Wire-shape sentinel matching the gateway's `KError` JSON payload. The HTTP client recognises this on non-2xx responses\n * and rehydrates it into a `KError` instance.\n */\nexport type KErrorWire = {\n code: string;\n message: string;\n context?: Record<string, unknown>;\n recommendation?: string | null;\n requestId?: string;\n};\n\n/**\n * The customer-facing error type. Every `@k9o/core` surface throws this\n * on non-2xx upstream responses, transport failures, or SDK-side\n * validation failures.\n */\nexport class KError extends Error {\n readonly code: string;\n readonly context: Readonly<Record<string, unknown>>;\n readonly recommendation: string | null;\n readonly status: number | null;\n readonly requestId: string | null;\n\n constructor(init: {\n code: string;\n message: string;\n context?: Record<string, unknown>;\n recommendation?: string | null;\n status?: number | null;\n requestId?: string | null;\n }) {\n super(init.message);\n this.name = \"KError\";\n this.code = init.code;\n this.context = Object.freeze({ ...(init.context ?? {}) });\n this.recommendation = init.recommendation ?? null;\n this.status = init.status ?? null;\n this.requestId = init.requestId ?? null;\n }\n}\n\n/**\n * Rehydrate a wire `KError` payload into a typed `KError` instance.\n * Unlike `@k9o/gate.gateErrorFromWire`, `@k9o/core` does not maintain\n * a hand-curated allow-list of known codes — the gateway's KError\n * code-space is open-ended and curated centrally per ch11; the SDK\n * preserves whatever code the gateway sends. Code-narrowing in caller\n * code uses string-literal comparison against the well-known codes\n * (e.g. `if (e.code === 'K9O_TIER_CAP_HIT')`).\n */\nexport function kerrorFromWire(wire: KErrorWire, status: number): KError {\n return new KError({\n code: wire.code,\n message: wire.message,\n context: wire.context ?? {},\n recommendation: wire.recommendation ?? null,\n status,\n requestId: wire.requestId ?? null,\n });\n}\n"],"mappings":";AAgDO,IAAM,SAAN,cAAqB,MAAM;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,MAOT;AACD,UAAM,KAAK,OAAO;AAClB,SAAK,OAAO;AACZ,SAAK,OAAO,KAAK;AACjB,SAAK,UAAU,OAAO,OAAO,EAAE,GAAI,KAAK,WAAW,CAAC,EAAG,CAAC;AACxD,SAAK,iBAAiB,KAAK,kBAAkB;AAC7C,SAAK,SAAS,KAAK,UAAU;AAC7B,SAAK,YAAY,KAAK,aAAa;AAAA,EACrC;AACF;AAWO,SAAS,eAAe,MAAkB,QAAwB;AACvE,SAAO,IAAI,OAAO;AAAA,IAChB,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,SAAS,KAAK,WAAW,CAAC;AAAA,IAC1B,gBAAgB,KAAK,kBAAkB;AAAA,IACvC;AAAA,IACA,WAAW,KAAK,aAAa;AAAA,EAC/B,CAAC;AACH;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/retry.ts","../src/region.ts","../src/http.ts"],"sourcesContent":["/**\n * Retry + backoff for the HTTP client.\n *\n * Configured via `retryConfig: { maxRetries, backoff }`. Retries are\n * scoped to transient failures only — never retry on KError-carrying\n * non-2xx responses (those are semantic; the caller must handle them).\n *\n * Retryable conditions (transient; safe to retry idempotently):\n * - Transport error (`K9O_UPSTREAM_UNAVAILABLE` from `request()`) — network down,\n * DNS fail, connection refused, etc.\n * - HTTP 502 / 503 / 504 (gateway-up but downstream unavailable)\n * - HTTP 429 with `Retry-After` header (rate-limit; backoff by header value)\n *\n * Non-retryable (semantic; caller must handle):\n * - HTTP 4xx other than 429 (caller bug; retry won't help)\n * - HTTP 5xx other than 502/503/504 (gateway-side bug; surface immediately)\n * - KError on success-shape (rare; malformed envelope)\n *\n * Retry method discipline: only GET / HEAD / OPTIONS / DELETE are\n * retried by default — these are RFC-7231 idempotent. POST / PATCH /\n * PUT are NOT retried (caller can opt in via `idempotencyKey` header\n * per ch01 §10.2 when they assert idempotency).\n *\n * Backoff:\n * - `exponential` (default) — `initialDelayMs * 2^attempt`, capped at maxDelayMs; jittered ±25%\n * - `linear` — `initialDelayMs * (attempt + 1)`, capped at maxDelayMs; jittered ±25%\n * - `none` — no delay between attempts\n */\n\nimport { KError } from \"./kerror.ts\";\nimport type { RetryConfig } from \"./types.ts\";\n\nconst RETRYABLE_METHODS = new Set([\"GET\", \"HEAD\", \"OPTIONS\", \"DELETE\"]);\nconst RETRYABLE_STATUS = new Set([502, 503, 504]);\n\nexport const DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxRetries: 3,\n backoff: \"exponential\",\n initialDelayMs: 100,\n maxDelayMs: 5000,\n};\n\n/**\n * Decide if a thrown error is retryable.\n * Only KError instances are inspected; other thrown values propagate\n * (programmer error / unrecoverable state).\n */\nexport function isRetryable(error: unknown, method: string): boolean {\n if (!RETRYABLE_METHODS.has(method.toUpperCase())) return false;\n if (!(error instanceof KError)) return false;\n if (error.code === \"K9O_UPSTREAM_UNAVAILABLE\") return true;\n if (error.status !== null && RETRYABLE_STATUS.has(error.status)) return true;\n if (error.status === 429) return true; // rate-limit; respect Retry-After if surfaced in context\n return false;\n}\n\n/**\n * Compute the backoff delay for a given attempt index (0-based).\n */\nexport function computeDelay(config: Required<RetryConfig>, attempt: number): number {\n let base: number;\n if (config.backoff === \"none\") return 0;\n if (config.backoff === \"linear\") base = config.initialDelayMs * (attempt + 1);\n else base = config.initialDelayMs * Math.pow(2, attempt);\n const capped = Math.min(base, config.maxDelayMs);\n // ±25% jitter\n const jitter = capped * 0.25 * (Math.random() * 2 - 1);\n return Math.max(0, Math.floor(capped + jitter));\n}\n\n/**\n * Sleep helper. Pulled out so tests can stub it via injection.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Execute a function with retry. The function is called up to\n * `maxRetries + 1` times; non-retryable failures throw immediately.\n *\n * @param fn - The function to execute (typically the HTTP request)\n * @param method - HTTP method (used to gate retry per RETRYABLE_METHODS)\n * @param config - Retry config; defaults applied if partial\n * @param sleepImpl - Sleep implementation (tests inject a no-op or fast variant)\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n method: string,\n config: RetryConfig | undefined,\n sleepImpl: (ms: number) => Promise<void> = sleep,\n): Promise<T> {\n const c: Required<RetryConfig> = {\n ...DEFAULT_RETRY_CONFIG,\n ...(config ?? {}),\n };\n let lastError: unknown;\n for (let attempt = 0; attempt <= c.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (e) {\n lastError = e;\n if (attempt === c.maxRetries) break;\n if (!isRetryable(e, method)) break;\n const delay = computeDelay(c, attempt);\n if (delay > 0) await sleepImpl(delay);\n }\n }\n throw lastError;\n}\n","/**\n * Regional host resolution lives in `@k9o/gate` (which `@k9o/core` composes), so the Gate client and the core client\n * share one implementation of the probe and of the `Location` validation. See `packages/gate/src/region.ts`.\n */\nexport { resolveRegionalOrigin, regionalOriginFromLocation, type RegionConfig } from \"@k9o/gate\";\n","/**\n * HTTP client abstraction for `@k9o/core`.\n *\n * Lifted + generalised from `@k9o/gate/client.ts` (the auth-pillar SDK's\n * proven pattern). This client is per-instance — `createClient` constructs\n * one bound to its config; per-call options merge into the bound defaults.\n *\n * Responsibilities:\n * - URL construction against bound `baseUrl` + per-call `path` + query\n * - Envelope unwrap on 2xx (rejects malformed envelopes as INTERNAL_ERROR)\n * - KError rehydration on non-2xx\n * - Bearer token injection (from per-call override OR bound default)\n * - Project context header (`x-k9o-project-slug`) injection when bound\n * - Retry with exponential backoff per RetryConfig (see retry.ts) — TBD\n * in next sub-cycle; v0.1.0 ships single-attempt\n *\n * The client is intentionally minimal — per-resource SDK methods compose\n * the path + body + bearer themselves and hand to `request()`. This keeps\n * the HTTP layer dumb and the per-resource logic auditable.\n */\n\nimport { KError, kerrorFromWire, type KErrorWire } from \"./kerror.ts\";\nimport type { Envelope } from \"./envelope.ts\";\nimport { withRetry } from \"./retry.ts\";\nimport type { RetryConfig } from \"./types.ts\";\nimport { resolveRegionalOrigin } from \"./region.ts\";\n\n/**\n * Per-instance HTTP client config. Constructed by `createClient`; passed\n * to `request()` per call.\n */\nexport type HttpClientConfig = {\n /** Normalised baseUrl (scheme + host); set via createClient. */\n baseUrl: string;\n /** Default bearer token (per-call override wins). */\n apiKey?: string | undefined;\n /** Active project slug — injected as `x-k9o-project-slug` header on every request. */\n projectSlug?: string | undefined;\n /** Active org slug — injected as `x-k9o-org-slug` header on every request. */\n orgSlug?: string | undefined;\n /** Custom fetch impl (defaults to globalThis.fetch); use for tests + non-standard runtimes. */\n fetch?: typeof globalThis.fetch | undefined;\n /** Default headers merged into every request. */\n defaultHeaders?: Record<string, string> | undefined;\n /** Retry config per ch13 §7.1; idempotent methods only (GET/HEAD/OPTIONS/DELETE) — applied when transport fails or upstream returns 502/503/504/429. */\n retryConfig?: RetryConfig | undefined;\n};\n\n/**\n * Per-call request options.\n */\nexport type RequestInit = {\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n body?: unknown;\n /** Per-call bearer override (wins over config.apiKey). */\n bearerToken?: string | undefined;\n /** Per-call additional headers (merged with config.defaultHeaders + auth). */\n headers?: Record<string, string> | undefined;\n /** Per-call query params. */\n query?: Record<string, string | number | boolean | undefined> | undefined;\n};\n\n/**\n * Issue a request against the gateway. Returns the unwrapped Envelope\n * on 2xx; throws KError on non-2xx, transport failures, or malformed\n * responses.\n */\nexport async function request<T>(\n config: HttpClientConfig,\n init: RequestInit,\n): Promise<Envelope<T>> {\n return withRetry(() => requestOnce<T>(config, init), init.method, config.retryConfig);\n}\n\n/**\n * Single-attempt request implementation. `request` wraps this in\n * retry-with-backoff per `config.retryConfig`. Exported for tests that\n * want to bypass retry semantics.\n */\nexport async function requestOnce<T>(\n config: HttpClientConfig,\n init: RequestInit,\n): Promise<Envelope<T>> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new KError({\n code: \"K9O_INTERNAL_ERROR\",\n message:\n \"No fetch implementation available — pass {fetch} in createClient options for runtimes without a global fetch\",\n context: {},\n recommendation: \"Use Node 18+, Bun 1.1+, or a modern browser; otherwise supply a fetch polyfill via the fetch option.\",\n });\n }\n\n const url = buildUrl(await resolveRegionalOrigin(config), init.path, init.query);\n const headers: Record<string, string> = {\n accept: \"application/json\",\n ...(config.defaultHeaders ?? {}),\n ...(config.projectSlug ? { \"x-k9o-project-slug\": config.projectSlug } : {}),\n ...(config.orgSlug ? { \"x-k9o-org-slug\": config.orgSlug } : {}),\n ...(init.headers ?? {}),\n };\n const bearer = init.bearerToken ?? config.apiKey;\n if (typeof bearer === \"string\" && bearer.length > 0) {\n headers[\"authorization\"] = `Bearer ${bearer}`;\n }\n const requestInit: Record<string, unknown> = {\n method: init.method,\n headers,\n };\n if (init.body !== undefined) {\n if (typeof FormData !== \"undefined\" && init.body instanceof FormData) {\n // Multipart: the runtime sets the content type (with its boundary); never JSON-encode it.\n requestInit[\"body\"] = init.body;\n } else {\n headers[\"content-type\"] = \"application/json\";\n requestInit[\"body\"] = JSON.stringify(init.body);\n }\n }\n\n let response: Response;\n try {\n response = await fetchImpl(url, requestInit as Parameters<typeof fetchImpl>[1]);\n } catch (e) {\n throw new KError({\n code: \"K9O_UPSTREAM_UNAVAILABLE\",\n message: `Network error reaching ${url}: ${e instanceof Error ? e.message : String(e)}`,\n context: { url },\n recommendation: \"Check connectivity to the gateway; retry with backoff if transient.\",\n });\n }\n\n // 204 No Content — synthesize an empty envelope (some DELETE / PUT endpoints).\n if (response.status === 204) {\n return { data: undefined as unknown as T, meta: {} };\n }\n\n let parsed: unknown;\n try {\n parsed = await response.json();\n } catch (e) {\n throw new KError({\n code: \"K9O_INTERNAL_ERROR\",\n message: `Failed to parse JSON response from ${url}: ${e instanceof Error ? e.message : String(e)}`,\n context: { url, status: response.status },\n status: response.status,\n });\n }\n\n if (response.status >= 200 && response.status < 300) {\n if (!isEnvelope<T>(parsed)) {\n throw new KError({\n code: \"K9O_INTERNAL_ERROR\",\n message: `Malformed envelope from ${url}: expected {data, meta}`,\n context: { url, status: response.status },\n status: response.status,\n });\n }\n return parsed;\n }\n\n if (isKErrorWire(parsed)) {\n throw kerrorFromWire(parsed, response.status);\n }\n throw new KError({\n code: \"K9O_INTERNAL_ERROR\",\n message: `Upstream returned ${response.status} with non-KError body`,\n context: { url, status: response.status, body: parsed },\n status: response.status,\n });\n}\n\n/**\n * Open a streaming request against the gateway. Returns the raw\n * `Response` on 2xx (caller consumes `response.body` as an SSE / chunked\n * stream); throws `KError` on non-2xx, transport failures, or a missing\n * body. Used by streaming surfaces — `client.compute.llms.invokeStream`\n * (ch07 §10.2) + `client.realtime.subscribe` SSE transport (ch07 §11.3).\n *\n * Unlike `request()`, this does NOT unwrap the JSON envelope or apply\n * retry — streaming responses are consumed incrementally and retry on a\n * half-consumed stream is unsafe.\n */\nexport async function requestStream(\n config: HttpClientConfig,\n init: RequestInit & { accept?: string },\n): Promise<Response> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new KError({\n code: \"K9O_INTERNAL_ERROR\",\n message: \"No fetch implementation available — pass {fetch} in createClient options\",\n context: {},\n recommendation: \"Use Node 18+, Bun 1.1+, or a modern browser; otherwise supply a fetch polyfill via the fetch option.\",\n });\n }\n\n const url = buildUrl(await resolveRegionalOrigin(config), init.path, init.query);\n const headers: Record<string, string> = {\n accept: init.accept ?? \"text/event-stream\",\n ...(config.defaultHeaders ?? {}),\n ...(config.projectSlug ? { \"x-k9o-project-slug\": config.projectSlug } : {}),\n ...(config.orgSlug ? { \"x-k9o-org-slug\": config.orgSlug } : {}),\n ...(init.headers ?? {}),\n };\n const bearer = init.bearerToken ?? config.apiKey;\n if (typeof bearer === \"string\" && bearer.length > 0) {\n headers[\"authorization\"] = `Bearer ${bearer}`;\n }\n const requestInit: Record<string, unknown> = { method: init.method, headers };\n if (init.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n requestInit[\"body\"] = JSON.stringify(init.body);\n }\n\n let response: Response;\n try {\n response = await fetchImpl(url, requestInit as Parameters<typeof fetchImpl>[1]);\n } catch (e) {\n throw new KError({\n code: \"K9O_UPSTREAM_UNAVAILABLE\",\n message: `Network error reaching ${url}: ${e instanceof Error ? e.message : String(e)}`,\n context: { url },\n recommendation: \"Check connectivity to the gateway; retry with backoff if transient.\",\n });\n }\n\n if (response.status < 200 || response.status >= 300) {\n let parsed: unknown = null;\n try {\n parsed = await response.json();\n } catch {\n /* non-JSON error body */\n }\n if (isKErrorWire(parsed)) throw kerrorFromWire(parsed, response.status);\n throw new KError({\n code: \"K9O_INTERNAL_ERROR\",\n message: `Upstream returned ${response.status} opening stream`,\n context: { url, status: response.status, body: parsed },\n status: response.status,\n });\n }\n\n if (response.body === null) {\n throw new KError({\n code: \"K9O_INTERNAL_ERROR\",\n message: `Stream response from ${url} has no body`,\n context: { url, status: response.status },\n status: response.status,\n });\n }\n\n return response;\n}\n\nfunction buildUrl(\n baseUrl: string,\n path: string,\n query: RequestInit[\"query\"],\n): string {\n const trimmedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n const trimmedPath = path.startsWith(\"/\") ? path : `/${path}`;\n let url = `${trimmedBase}${trimmedPath}`;\n if (query !== undefined) {\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) params.set(k, String(v));\n }\n const qs = params.toString();\n if (qs.length > 0) url += `?${qs}`;\n }\n return url;\n}\n\nfunction isEnvelope<T>(value: unknown): value is Envelope<T> {\n if (value === null || typeof value !== \"object\") return false;\n const v = value as { data?: unknown; meta?: unknown };\n return \"data\" in v && \"meta\" in v && typeof v.meta === \"object\" && v.meta !== null;\n}\n\nfunction isKErrorWire(value: unknown): value is KErrorWire {\n if (value === null || typeof value !== \"object\") return false;\n const v = value as { code?: unknown; message?: unknown };\n return typeof v.code === \"string\" && typeof v.message === \"string\";\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/url-grammar.ts"],"sourcesContent":["/**\n * URL grammar v2.5.68.\n *\n * Canonical URL shapes:\n * - Member-plane (project-scoped): `<orgSlug>.api.k9o.io/@<projectSlug>/<sub-resource>/...`\n * - Org-plane (org-scoped): `<orgSlug>.api.k9o.io/<resource>/...`\n * - Platform-plane (cross-org): `api.k9o.io/discovery/...` + `api.k9o.io/auth/...` etc.\n *\n * The `baseUrl` passed to `createClient` is the gateway host root —\n * scheme + host (+ optional port) per ch13 §7. Path-bearing baseUrls\n * are rejected with `K9O_INVALID_BASE_URL` per ch13 §7.2.\n *\n * The SDK constructs URLs by composing `baseUrl + path-from-builder`.\n * Per-resource SDK methods use the builders below to assemble their\n * `path` argument before handing to the HTTP client.\n */\n\nimport { KError } from \"./kerror.ts\";\n\n/**\n * Validate a `baseUrl` per ch13 §7.2. Returns the normalised form\n * (trailing-slash stripped) on success; throws `K9O_INVALID_BASE_URL`\n * on any violation.\n *\n * Accepted shapes:\n * - `https://api.k9o.io`\n * - `https://acme.api.k9o.io`\n * - `http://localhost:8080`\n * - `https://api.k9o.io:443`\n *\n * Rejected shapes:\n * - path segments (`https://api.k9o.io/v2`)\n * - missing scheme (`api.k9o.io`)\n * - non-http(s) scheme (`wss://api.k9o.io`)\n * - query / fragment (`https://api.k9o.io?x=1`)\n */\nexport function validateBaseUrl(baseUrl: string): string {\n let parsed: URL;\n try {\n parsed = new URL(baseUrl);\n } catch {\n throw new KError({\n code: \"K9O_INVALID_BASE_URL\",\n message: `baseUrl \"${baseUrl}\" is not a valid URL`,\n context: { baseUrl },\n recommendation: \"Pass a scheme + host like `https://api.k9o.io` or `https://<orgSlug>.api.k9o.io`\",\n status: null,\n });\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new KError({\n code: \"K9O_INVALID_BASE_URL\",\n message: `baseUrl \"${baseUrl}\" uses unsupported scheme \"${parsed.protocol}\"; expected http: or https:`,\n context: { baseUrl, scheme: parsed.protocol },\n recommendation: \"Use http:// or https://. WebSocket subscriptions are wired internally over SSE on the same host.\",\n status: null,\n });\n }\n if (parsed.pathname !== \"/\" && parsed.pathname !== \"\") {\n throw new KError({\n code: \"K9O_INVALID_BASE_URL\",\n message: `baseUrl \"${baseUrl}\" must not contain a path segment; got \"${parsed.pathname}\"`,\n context: { baseUrl, path: parsed.pathname },\n recommendation: \"Strip the path segment — pass host only. URL paths are composed by the SDK.\",\n status: null,\n });\n }\n if (parsed.search.length > 0 || parsed.hash.length > 0) {\n throw new KError({\n code: \"K9O_INVALID_BASE_URL\",\n message: `baseUrl \"${baseUrl}\" must not contain query or fragment`,\n context: { baseUrl, query: parsed.search, fragment: parsed.hash },\n recommendation: \"Pass host only.\",\n status: null,\n });\n }\n // Normalise — strip trailing slash so concatenation is predictable.\n const normalised = `${parsed.protocol}//${parsed.host}`;\n return normalised;\n}\n\n/**\n * The bare platform apex hosts org-subdomain derivation applies to. `api.k9o.io`\n * (production) and `api-staging.k9o.io` (staging) share the same\n * `<orgSlug>.<apex>` wire-routing scheme — confirmed live (conformance,\n * `k9o login` examples, staging demo configs all address orgs this way on\n * both). A THIRD apex here needs the same confirmation before joining this\n * set — never inferred from a hostname merely looking apex-shaped.\n */\nconst K9O_ORG_DERIVABLE_APEXES = new Set([\"api.k9o.io\", \"api-staging.k9o.io\"]);\n\n/**\n * Resolve the effective baseUrl. `baseUrl` is\n * OPTIONAL — it defaults to the platform apex `https://api.k9o.io`. When the\n * (validated) host is a bare apex in `K9O_ORG_DERIVABLE_APEXES` and `orgSlug`\n * is supplied, the SDK composes the canonical org-subdomain wire root\n * `https://<orgSlug>.<apex>` (ch01 §5.4). An explicit `<orgSlug>` subdomain, a\n * self-hosted / Sovereign host, or a dev host (anything not in that set)\n * passes through unchanged — `orgSlug` is intentionally NOT validated or\n * composed onto a non-apex host (fixed 2026-08-30: this used to silently\n * ignore `orgSlug` for `api-staging.k9o.io` too, not just genuine\n * self-hosted/custom hosts, since only the literal production apex string\n * was checked).\n */\nexport function resolveBaseUrl(baseUrl: string | undefined, orgSlug?: string): string {\n const validated = validateBaseUrl(baseUrl ?? \"https://api.k9o.io\");\n const parsed = new URL(validated);\n if (K9O_ORG_DERIVABLE_APEXES.has(parsed.hostname) && orgSlug) {\n validateSlug(orgSlug, \"org\");\n return `${parsed.protocol}//${orgSlug}.${parsed.hostname}`;\n }\n return validated;\n}\n\n/**\n * Slug format validator:\n * `[a-z][a-z0-9-]{1,62}` and must not end with `-`.\n */\nconst SLUG_RE = /^[a-z][a-z0-9-]{1,62}$/;\n\nexport function validateSlug(slug: string, kind: \"org\" | \"project\"): void {\n if (!SLUG_RE.test(slug) || slug.endsWith(\"-\")) {\n throw new KError({\n code: \"K9O_INVALID_FIELD_VALUE\",\n message: `${kind}Slug \"${slug}\" does not match required format ^[a-z][a-z0-9-]{1,62}$ (must not end with '-')`,\n context: { slug, kind },\n recommendation: `Pass a slug matching the format — lowercase letters / digits / hyphens; 2-63 chars; starts with a letter; does not end with hyphen.`,\n status: null,\n });\n }\n}\n\n/**\n * Build a project-scoped (member-plane) URL path per v2.5.68 §5.4.\n * E.g. `projectPath('member-portal', 'collections/posts/documents')`\n * → `/@member-portal/collections/posts/documents`.\n *\n * The caller is responsible for normalising sub-resource components\n * (URL-encoding goes through the caller's path construction; this\n * helper handles only the prefix + slug shape).\n */\nexport function projectPath(projectSlug: string, subResource: string): string {\n validateSlug(projectSlug, \"project\");\n const cleaned = subResource.startsWith(\"/\") ? subResource.slice(1) : subResource;\n return `/@${projectSlug}/${cleaned}`;\n}\n\n/**\n * Build an org-plane path. E.g. `orgPath('org')` → `/org`,\n * `orgPath('projects')` → `/projects`.\n */\nexport function orgPath(resource: string): string {\n const cleaned = resource.startsWith(\"/\") ? resource.slice(1) : resource;\n return `/${cleaned}`;\n}\n\n/**\n * Build a platform-plane path (cross-org). E.g.\n * `platformPath('discovery/projects')` → `/discovery/projects`,\n * `platformPath('auth/sign-in')` → `/auth/sign-in`.\n *\n * Composes with the platform baseUrl (`https://api.k9o.io`) — not the\n * org-subdomain baseUrl.\n */\nexport function platformPath(resource: string): string {\n const cleaned = resource.startsWith(\"/\") ? resource.slice(1) : resource;\n return `/${cleaned}`;\n}\n"],"mappings":";;;;;AAoCO,SAAS,gBAAgB,SAAyB;AACvD,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,OAAO;AAAA,EAC1B,QAAQ;AACN,UAAM,IAAI,OAAO;AAAA,MACf,MAAM;AAAA,MACN,SAAS,YAAY,OAAO;AAAA,MAC5B,SAAS,EAAE,QAAQ;AAAA,MACnB,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,MAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UAAU;AAC/D,UAAM,IAAI,OAAO;AAAA,MACf,MAAM;AAAA,MACN,SAAS,YAAY,OAAO,8BAA8B,OAAO,QAAQ;AAAA,MACzE,SAAS,EAAE,SAAS,QAAQ,OAAO,SAAS;AAAA,MAC5C,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,MAAI,OAAO,aAAa,OAAO,OAAO,aAAa,IAAI;AACrD,UAAM,IAAI,OAAO;AAAA,MACf,MAAM;AAAA,MACN,SAAS,YAAY,OAAO,2CAA2C,OAAO,QAAQ;AAAA,MACtF,SAAS,EAAE,SAAS,MAAM,OAAO,SAAS;AAAA,MAC1C,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,MAAI,OAAO,OAAO,SAAS,KAAK,OAAO,KAAK,SAAS,GAAG;AACtD,UAAM,IAAI,OAAO;AAAA,MACf,MAAM;AAAA,MACN,SAAS,YAAY,OAAO;AAAA,MAC5B,SAAS,EAAE,SAAS,OAAO,OAAO,QAAQ,UAAU,OAAO,KAAK;AAAA,MAChE,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,QAAM,aAAa,GAAG,OAAO,QAAQ,KAAK,OAAO,IAAI;AACrD,SAAO;AACT;AAUA,IAAM,2BAA2B,oBAAI,IAAI,CAAC,cAAc,oBAAoB,CAAC;AAetE,SAAS,eAAe,SAA6B,SAA0B;AACpF,QAAM,YAAY,gBAAgB,WAAW,oBAAoB;AACjE,QAAM,SAAS,IAAI,IAAI,SAAS;AAChC,MAAI,yBAAyB,IAAI,OAAO,QAAQ,KAAK,SAAS;AAC5D,iBAAa,SAAS,KAAK;AAC3B,WAAO,GAAG,OAAO,QAAQ,KAAK,OAAO,IAAI,OAAO,QAAQ;AAAA,EAC1D;AACA,SAAO;AACT;AAMA,IAAM,UAAU;AAET,SAAS,aAAa,MAAc,MAA+B;AACxE,MAAI,CAAC,QAAQ,KAAK,IAAI,KAAK,KAAK,SAAS,GAAG,GAAG;AAC7C,UAAM,IAAI,OAAO;AAAA,MACf,MAAM;AAAA,MACN,SAAS,GAAG,IAAI,SAAS,IAAI;AAAA,MAC7B,SAAS,EAAE,MAAM,KAAK;AAAA,MACtB,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACF;AAWO,SAAS,YAAY,aAAqB,aAA6B;AAC5E,eAAa,aAAa,SAAS;AACnC,QAAM,UAAU,YAAY,WAAW,GAAG,IAAI,YAAY,MAAM,CAAC,IAAI;AACrE,SAAO,KAAK,WAAW,IAAI,OAAO;AACpC;AAMO,SAAS,QAAQ,UAA0B;AAChD,QAAM,UAAU,SAAS,WAAW,GAAG,IAAI,SAAS,MAAM,CAAC,IAAI;AAC/D,SAAO,IAAI,OAAO;AACpB;AAUO,SAAS,aAAa,UAA0B;AACrD,QAAM,UAAU,SAAS,WAAW,GAAG,IAAI,SAAS,MAAM,CAAC,IAAI;AAC/D,SAAO,IAAI,OAAO;AACpB;","names":[]}
|