@consciousclouds/canvas-sdk 0.2.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 (39) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +29 -0
  3. package/dist/client/canvas-client.d.ts +28 -0
  4. package/dist/client/canvas-client.d.ts.map +1 -0
  5. package/dist/client/canvas-client.js +50 -0
  6. package/dist/client/canvas-client.js.map +1 -0
  7. package/dist/contracts/experience-adapter.d.ts +83 -0
  8. package/dist/contracts/experience-adapter.d.ts.map +1 -0
  9. package/dist/contracts/experience-adapter.js +50 -0
  10. package/dist/contracts/experience-adapter.js.map +1 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +5 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/registry/component-registry.d.ts +40 -0
  16. package/dist/registry/component-registry.d.ts.map +1 -0
  17. package/dist/registry/component-registry.js +59 -0
  18. package/dist/registry/component-registry.js.map +1 -0
  19. package/dist/storage/client.d.ts +70 -0
  20. package/dist/storage/client.d.ts.map +1 -0
  21. package/dist/storage/client.js +146 -0
  22. package/dist/storage/client.js.map +1 -0
  23. package/dist/storage/index.d.ts +13 -0
  24. package/dist/storage/index.d.ts.map +1 -0
  25. package/dist/storage/index.js +11 -0
  26. package/dist/storage/index.js.map +1 -0
  27. package/dist/storage/types.d.ts +80 -0
  28. package/dist/storage/types.d.ts.map +1 -0
  29. package/dist/storage/types.js +11 -0
  30. package/dist/storage/types.js.map +1 -0
  31. package/dist/types/canvas.d.ts +94 -0
  32. package/dist/types/canvas.d.ts.map +1 -0
  33. package/dist/types/canvas.js +11 -0
  34. package/dist/types/canvas.js.map +1 -0
  35. package/dist/validate/canvas.d.ts +1091 -0
  36. package/dist/validate/canvas.d.ts.map +1 -0
  37. package/dist/validate/canvas.js +120 -0
  38. package/dist/validate/canvas.js.map +1 -0
  39. package/package.json +60 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog — @consciousclouds/canvas-sdk
2
+
3
+ Versioned on the independent frontend-SDK lifecycle (NOT `contract_version`).
4
+
5
+ ## [0.1.0] — 2026-06-09
6
+
7
+ ### Added
8
+
9
+ - `CanvasDefinition` types (discriminated union: `stat-card`, `data-table`,
10
+ `service-health`), derived from `platform/contracts/canvas/v1/`.
11
+ - Component Registry allowlist (`stat-card`, `data-table`, `service-health`)
12
+ with `active | deprecated | removed` lifecycle.
13
+ - `IExperienceProvider` adapter contract + `ExperienceAdapterRegistry`
14
+ (explicit registration, fail-closed on unknown product).
15
+ - `validateCanvasDefinition()` (zod) and a thin `createCanvasClient()`.
16
+
17
+ ### Notes
18
+
19
+ - Authoritative contract is the JSON Schema under
20
+ `platform/contracts/canvas/v1/`. These TypeScript types mirror it; the
21
+ conformance test guards drift. Full schema→TS codegen is a follow-up.
22
+ - Phase 1 slice: deterministic selection only, no LLM, no persistence.
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @consciousclouds/canvas-sdk
2
+
3
+ Types + contracts for the Intelligence Runtime **Canvas** layer. The server
4
+ emits a `CanvasDefinition`; a consumer renders it from an **approved
5
+ component allowlist** (`@consciousclouds/canvas-runtime` is the React
6
+ renderer). The model may *select* components; it may never *invent* UI.
7
+
8
+ Framework-agnostic and domain-agnostic — no product nouns, no React. The
9
+ authoritative contract is the JSON Schema under
10
+ `platform/contracts/canvas/v1/`; these types derive from it.
11
+
12
+ ## Exports
13
+
14
+ | Subpath | What |
15
+ |---|---|
16
+ | `.` | everything below |
17
+ | `./types` | `CanvasDefinition`, `CanvasNode`, node + prop types |
18
+ | `./registry` | `ComponentRegistry`, `COMPONENT_REGISTRY`, `isApprovedComponent()` |
19
+ | `./contracts` | `IExperienceProvider`, `ExperienceAdapterRegistry`, scoping types |
20
+ | `./validate` | `validateCanvasDefinition()`, `NODE_TYPES` |
21
+ | `./client` | `createCanvasClient()` — thin HTTP wrapper |
22
+
23
+ ## Adapter authors (e.g. ENO)
24
+
25
+ Implement `IExperienceProvider` (domain methods like `getWine()` live in your
26
+ concrete adapter, never in this SDK), register it explicitly at your
27
+ composition root, and let the runtime call it. The runtime authorizes every
28
+ request against the *caller's* identity (see the design lock's four-gate
29
+ model) — your adapter never decides authorization.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Thin canvas client — types + HTTP wrapper only (the locked SDK rule).
3
+ * No business logic, no caching, no orchestration.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ import type { CanvasDefinition } from '../types/canvas.js';
8
+ export interface CanvasClientOptions {
9
+ /** Base URL of the gateway, e.g. https://gateway.example.com */
10
+ baseUrl: string;
11
+ /** Optional fetch implementation (defaults to global fetch). */
12
+ fetchImpl?: typeof fetch;
13
+ /** Optional per-request headers (auth, correlation, tenant). */
14
+ headers?: () => Record<string, string> | Promise<Record<string, string>>;
15
+ /** Validate the response against the schema (default true). */
16
+ validate?: boolean;
17
+ }
18
+ export interface CanvasClient {
19
+ /** GET /api/v1/canvas/:product/:resource → CanvasDefinition */
20
+ getCanvas(product: string, resource: string, params?: Record<string, string>): Promise<CanvasDefinition>;
21
+ }
22
+ export declare class CanvasClientError extends Error {
23
+ readonly status?: number | undefined;
24
+ readonly details?: unknown | undefined;
25
+ constructor(message: string, status?: number | undefined, details?: unknown | undefined);
26
+ }
27
+ export declare function createCanvasClient(options: CanvasClientOptions): CanvasClient;
28
+ //# sourceMappingURL=canvas-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-client.d.ts","sourceRoot":"","sources":["../../src/client/canvas-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzE,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,SAAS,CACP,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC9B;AAED,qBAAa,iBAAkB,SAAQ,KAAK;aAGxB,MAAM,CAAC,EAAE,MAAM;aACf,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,OAAO,CAAC,EAAE,OAAO,YAAA;CAKpC;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAoC7E"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Thin canvas client — types + HTTP wrapper only (the locked SDK rule).
3
+ * No business logic, no caching, no orchestration.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ import { validateCanvasDefinition } from '../validate/canvas.js';
8
+ export class CanvasClientError extends Error {
9
+ constructor(message, status, details) {
10
+ super(message);
11
+ this.status = status;
12
+ this.details = details;
13
+ this.name = 'CanvasClientError';
14
+ }
15
+ }
16
+ export function createCanvasClient(options) {
17
+ const base = options.baseUrl.replace(/\/$/, '');
18
+ const doFetch = options.fetchImpl ?? globalThis.fetch;
19
+ const validate = options.validate ?? true;
20
+ return {
21
+ async getCanvas(product, resource, params) {
22
+ const qs = params ? `?${new URLSearchParams(params).toString()}` : '';
23
+ const url = `${base}/api/v1/canvas/${encodeURIComponent(product)}/${encodeURIComponent(resource)}${qs}`;
24
+ const headers = options.headers ? await options.headers() : {};
25
+ const res = await doFetch(url, {
26
+ method: 'GET',
27
+ headers: { Accept: 'application/json', ...headers },
28
+ });
29
+ if (!res.ok) {
30
+ let body;
31
+ try {
32
+ body = await res.json();
33
+ }
34
+ catch {
35
+ body = undefined;
36
+ }
37
+ throw new CanvasClientError(`Canvas request failed (${res.status})`, res.status, body);
38
+ }
39
+ const json = (await res.json());
40
+ if (!validate)
41
+ return json;
42
+ const result = validateCanvasDefinition(json);
43
+ if (!result.ok || !result.data) {
44
+ throw new CanvasClientError('Canvas response failed validation', res.status, result.errors);
45
+ }
46
+ return result.data;
47
+ },
48
+ };
49
+ }
50
+ //# sourceMappingURL=canvas-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-client.js","sourceRoot":"","sources":["../../src/client/canvas-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAsBjE,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YACE,OAAe,EACC,MAAe,EACf,OAAiB;QAEjC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAS;QACf,YAAO,GAAP,OAAO,CAAU;QAGjC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAAC,OAA4B;IAC7D,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACtD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;IAE1C,OAAO;QACL,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM;YACvC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,MAAM,GAAG,GAAG,GAAG,IAAI,kBAAkB,kBAAkB,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC;YACxG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAE/D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;gBAC7B,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,GAAG,OAAO,EAAE;aACpD,CAAC,CAAC;YAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,IAAI,IAAa,CAAC;gBAClB,IAAI,CAAC;oBACH,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC1B,CAAC;gBAAC,MAAM,CAAC;oBACP,IAAI,GAAG,SAAS,CAAC;gBACnB,CAAC;gBACD,MAAM,IAAI,iBAAiB,CAAC,0BAA0B,GAAG,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACzF,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAY,CAAC;YAC3C,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAwB,CAAC;YAE/C,MAAM,MAAM,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC/B,MAAM,IAAI,iBAAiB,CAAC,mCAAmC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9F,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * IExperienceProvider — the per-product adapter contract.
3
+ *
4
+ * Modeled 1:1 on runtime-orchestrator's IRuntimeAdapter (strategy pattern,
5
+ * idempotent by correlationId, explicit registration). Domain-agnostic: no
6
+ * product nouns appear here — getWine()/getEnvironment() live ONLY in the
7
+ * concrete adapter a product ships. The runtime calls adapters, never an app
8
+ * database.
9
+ *
10
+ * Terminology lock: `provider` = this interface; `adapter` = a concrete impl.
11
+ *
12
+ * @packageDocumentation
13
+ */
14
+ /** Registry key for a registered product (closed at registration, not in the contract). */
15
+ export type ProductId = string;
16
+ /**
17
+ * Scope binding declared at registration. Maps to consul scope_type
18
+ * (only `global | tenant` exist) plus the project_id boundary.
19
+ */
20
+ export interface AdapterScopeBinding {
21
+ /** consul RBAC scope. There is no `platform` scope_type in the platform. */
22
+ scopeType: 'global' | 'tenant';
23
+ /** When true, the request MUST carry projectId and passes the project-access gate. */
24
+ projectBound?: boolean;
25
+ }
26
+ /**
27
+ * Per-request context. Extends node-sdk RequestContext fields; the runtime
28
+ * fills it from the *caller's* identity (never the runtime's own). Idempotent
29
+ * by correlationId.
30
+ */
31
+ export interface ExperienceContext {
32
+ correlationId: string;
33
+ tenantId: string;
34
+ userId: string;
35
+ projectId?: string;
36
+ /** Requested view/resource key (allowlisted by the runtime). */
37
+ resource: string;
38
+ params?: Record<string, unknown>;
39
+ }
40
+ /** Product-neutral data the runtime reads to assemble a context package. */
41
+ export interface ExperienceResult {
42
+ data: Record<string, unknown>;
43
+ metadata?: Record<string, unknown>;
44
+ }
45
+ /** The registered-adapter interface (parallels IRuntimeAdapter). */
46
+ export interface IExperienceProvider {
47
+ /** Registry key + log routing (cf. IRuntimeAdapter.name). */
48
+ readonly productId: ProductId;
49
+ /** Bound at registration, immutable. */
50
+ readonly scope: AdapterScopeBinding;
51
+ /** Read product data. IDEMPOTENT by correlationId. */
52
+ fetch(ctx: ExperienceContext): Promise<ExperienceResult>;
53
+ /** Optional mutating action (requires adapter.invoke + non-read project role). */
54
+ action?(ctx: ExperienceContext): Promise<ExperienceResult>;
55
+ /** Clear idempotency cache for a correlation id (same hook as IRuntimeAdapter). */
56
+ clearCache?(correlationId: string): void;
57
+ }
58
+ export interface IExperienceAdapterRegistry {
59
+ set(productId: ProductId, adapter: IExperienceProvider): void;
60
+ /** Throws ProductNotRegisteredError if absent — fail closed, no default. */
61
+ get(productId: ProductId): IExperienceProvider;
62
+ has(productId: ProductId): boolean;
63
+ list(): ProductId[];
64
+ }
65
+ export declare class ProductNotRegisteredError extends Error {
66
+ readonly productId: ProductId;
67
+ readonly code = "PRODUCT_NOT_REGISTERED";
68
+ constructor(productId: ProductId);
69
+ }
70
+ /**
71
+ * Closed-allowlist adapter registry. Unlike the runtime-orchestrator factory
72
+ * there is NO lazy-create fallback — every product adapter requires external
73
+ * config, so an unregistered product fails closed.
74
+ */
75
+ export declare class ExperienceAdapterRegistry implements IExperienceAdapterRegistry {
76
+ private readonly adapters;
77
+ set(productId: ProductId, adapter: IExperienceProvider): void;
78
+ get(productId: ProductId): IExperienceProvider;
79
+ has(productId: ProductId): boolean;
80
+ list(): ProductId[];
81
+ }
82
+ export declare function createExperienceAdapterRegistry(): ExperienceAdapterRegistry;
83
+ //# sourceMappingURL=experience-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experience-adapter.d.ts","sourceRoot":"","sources":["../../src/contracts/experience-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,2FAA2F;AAC3F,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC/B,sFAAsF;IACtF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,oEAAoE;AACpE,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC,sDAAsD;IACtD,KAAK,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzD,kFAAkF;IAClF,MAAM,CAAC,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3D,mFAAmF;IACnF,UAAU,CAAC,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,0BAA0B;IACzC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC9D,4EAA4E;IAC5E,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,mBAAmB,CAAC;IAC/C,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC;IACnC,IAAI,IAAI,SAAS,EAAE,CAAC;CACrB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;aAEtB,SAAS,EAAE,SAAS;IADhD,QAAQ,CAAC,IAAI,4BAA4B;gBACb,SAAS,EAAE,SAAS;CAIjD;AAED;;;;GAIG;AACH,qBAAa,yBAA0B,YAAW,0BAA0B;IAC1E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6C;IAEtE,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAI7D,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,mBAAmB;IAM9C,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO;IAIlC,IAAI,IAAI,SAAS,EAAE;CAGpB;AAED,wBAAgB,+BAA+B,IAAI,yBAAyB,CAE3E"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * IExperienceProvider — the per-product adapter contract.
3
+ *
4
+ * Modeled 1:1 on runtime-orchestrator's IRuntimeAdapter (strategy pattern,
5
+ * idempotent by correlationId, explicit registration). Domain-agnostic: no
6
+ * product nouns appear here — getWine()/getEnvironment() live ONLY in the
7
+ * concrete adapter a product ships. The runtime calls adapters, never an app
8
+ * database.
9
+ *
10
+ * Terminology lock: `provider` = this interface; `adapter` = a concrete impl.
11
+ *
12
+ * @packageDocumentation
13
+ */
14
+ export class ProductNotRegisteredError extends Error {
15
+ constructor(productId) {
16
+ super(`No experience adapter registered for product '${productId}'`);
17
+ this.productId = productId;
18
+ this.code = 'PRODUCT_NOT_REGISTERED';
19
+ this.name = 'ProductNotRegisteredError';
20
+ }
21
+ }
22
+ /**
23
+ * Closed-allowlist adapter registry. Unlike the runtime-orchestrator factory
24
+ * there is NO lazy-create fallback — every product adapter requires external
25
+ * config, so an unregistered product fails closed.
26
+ */
27
+ export class ExperienceAdapterRegistry {
28
+ constructor() {
29
+ this.adapters = new Map();
30
+ }
31
+ set(productId, adapter) {
32
+ this.adapters.set(productId, adapter);
33
+ }
34
+ get(productId) {
35
+ const adapter = this.adapters.get(productId);
36
+ if (!adapter)
37
+ throw new ProductNotRegisteredError(productId);
38
+ return adapter;
39
+ }
40
+ has(productId) {
41
+ return this.adapters.has(productId);
42
+ }
43
+ list() {
44
+ return [...this.adapters.keys()];
45
+ }
46
+ }
47
+ export function createExperienceAdapterRegistry() {
48
+ return new ExperienceAdapterRegistry();
49
+ }
50
+ //# sourceMappingURL=experience-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experience-adapter.js","sourceRoot":"","sources":["../../src/contracts/experience-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA2DH,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAElD,YAA4B,SAAoB;QAC9C,KAAK,CAAC,iDAAiD,SAAS,GAAG,CAAC,CAAC;QAD3C,cAAS,GAAT,SAAS,CAAW;QADvC,SAAI,GAAG,wBAAwB,CAAC;QAGvC,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,yBAAyB;IAAtC;QACmB,aAAQ,GAAG,IAAI,GAAG,EAAkC,CAAC;IAmBxE,CAAC;IAjBC,GAAG,CAAC,SAAoB,EAAE,OAA4B;QACpD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,GAAG,CAAC,SAAoB;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,yBAAyB,CAAC,SAAS,CAAC,CAAC;QAC7D,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,GAAG,CAAC,SAAoB;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC;CACF;AAED,MAAM,UAAU,+BAA+B;IAC7C,OAAO,IAAI,yBAAyB,EAAE,CAAC;AACzC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type { CanvasVersion, Binding, ActionDef, CanvasNodeBase, StatCardProps, StatCardNode, DataTableColumn, DataTableProps, DataTableNode, ServiceHealthItem, ServiceHealthProps, ServiceHealthNode, CanvasNode, CanvasNodeType, CanvasLayout, CanvasDefinition, } from './types/canvas.js';
2
+ export type { ComponentStatus, ComponentRegistryEntry, ComponentRegistry, } from './registry/component-registry.js';
3
+ export { COMPONENT_REGISTRY, getComponentEntry, isApprovedComponent, isDeprecatedComponent, } from './registry/component-registry.js';
4
+ export type { ProductId, AdapterScopeBinding, ExperienceContext, ExperienceResult, IExperienceProvider, IExperienceAdapterRegistry, } from './contracts/experience-adapter.js';
5
+ export { ProductNotRegisteredError, ExperienceAdapterRegistry, createExperienceAdapterRegistry, } from './contracts/experience-adapter.js';
6
+ export type { ValidationResult } from './validate/canvas.js';
7
+ export { NODE_TYPES, ZOD_NODE_TYPES, canvasNodeSchema, canvasDefinitionSchema, validateCanvasDefinition, } from './validate/canvas.js';
8
+ export type { CanvasClient, CanvasClientOptions } from './client/canvas-client.js';
9
+ export { createCanvasClient, CanvasClientError } from './client/canvas-client.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,OAAO,EACP,SAAS,EACT,cAAc,EACd,aAAa,EACb,YAAY,EACZ,eAAe,EACf,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,YAAY,EACZ,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,eAAe,EACf,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;AAG1C,YAAY,EACV,SAAS,EACT,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,mCAAmC,CAAC;AAG3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EACL,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { COMPONENT_REGISTRY, getComponentEntry, isApprovedComponent, isDeprecatedComponent, } from './registry/component-registry.js';
2
+ export { ProductNotRegisteredError, ExperienceAdapterRegistry, createExperienceAdapterRegistry, } from './contracts/experience-adapter.js';
3
+ export { NODE_TYPES, ZOD_NODE_TYPES, canvasNodeSchema, canvasDefinitionSchema, validateCanvasDefinition, } from './validate/canvas.js';
4
+ export { createCanvasClient, CanvasClientError } from './client/canvas-client.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA0BA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;AAW1C,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,mCAAmC,CAAC;AAI3C,OAAO,EACL,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Component Registry — the approved-component allowlist (GOVERNANCE).
3
+ *
4
+ * The model SELECTS from this; it cannot invent UI. The authoritative copy
5
+ * lives at platform/contracts/canvas/v1/component-registry.json; this is the
6
+ * SDK's typed, embedded copy (the contract JSON is not shipped in the
7
+ * published npm package). The conformance test asserts the two agree.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ import type { CanvasNodeType } from '../types/canvas.js';
12
+ export type ComponentStatus = 'active' | 'deprecated' | 'removed';
13
+ export interface ComponentRegistryEntry {
14
+ status: ComponentStatus;
15
+ since: string;
16
+ required_permission?: string | null;
17
+ props_schema?: string;
18
+ deprecated_since?: string;
19
+ replaced_by?: string;
20
+ removal_after?: string;
21
+ _note?: string;
22
+ }
23
+ export interface ComponentRegistry {
24
+ registry_version: string;
25
+ updated_at: string;
26
+ components: Record<string, ComponentRegistryEntry>;
27
+ }
28
+ /** Embedded allowlist — keep in sync with the contracts manifest (test-guarded). */
29
+ export declare const COMPONENT_REGISTRY: ComponentRegistry;
30
+ /** Look up an entry by component id (returns undefined if absent). */
31
+ export declare function getComponentEntry(id: string, registry?: ComponentRegistry): ComponentRegistryEntry | undefined;
32
+ /**
33
+ * True if a component id may be selected (emitter) or rendered (renderer):
34
+ * it must exist and be `active` or `deprecated`. `removed` and unknown ids
35
+ * fail closed.
36
+ */
37
+ export declare function isApprovedComponent(id: string, registry?: ComponentRegistry): id is CanvasNodeType;
38
+ /** True if an approved component is deprecated (still renders, with a warning). */
39
+ export declare function isDeprecatedComponent(id: string, registry?: ComponentRegistry): boolean;
40
+ //# sourceMappingURL=component-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-registry.d.ts","sourceRoot":"","sources":["../../src/registry/component-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAC;AAElE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CACpD;AAED,oFAAoF;AACpF,eAAO,MAAM,kBAAkB,EAAE,iBA6BhC,CAAC;AAEF,sEAAsE;AACtE,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,MAAM,EACV,QAAQ,GAAE,iBAAsC,GAC/C,sBAAsB,GAAG,SAAS,CAEpC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,MAAM,EACV,QAAQ,GAAE,iBAAsC,GAC/C,EAAE,IAAI,cAAc,CAGtB;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,QAAQ,GAAE,iBAAsC,GAC/C,OAAO,CAET"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Component Registry — the approved-component allowlist (GOVERNANCE).
3
+ *
4
+ * The model SELECTS from this; it cannot invent UI. The authoritative copy
5
+ * lives at platform/contracts/canvas/v1/component-registry.json; this is the
6
+ * SDK's typed, embedded copy (the contract JSON is not shipped in the
7
+ * published npm package). The conformance test asserts the two agree.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ /** Embedded allowlist — keep in sync with the contracts manifest (test-guarded). */
12
+ export const COMPONENT_REGISTRY = {
13
+ registry_version: '0.2.0',
14
+ updated_at: '2026-06-15T00:00:00Z',
15
+ components: {
16
+ 'stat-card': {
17
+ status: 'active',
18
+ since: '0.1.0',
19
+ required_permission: null,
20
+ props_schema: 'nodes/stat-card.schema.json',
21
+ },
22
+ 'data-table': {
23
+ status: 'active',
24
+ since: '0.1.0',
25
+ required_permission: null,
26
+ props_schema: 'nodes/data-table.schema.json',
27
+ },
28
+ 'service-health': {
29
+ status: 'active',
30
+ since: '0.1.0',
31
+ required_permission: null,
32
+ props_schema: 'nodes/service-health.schema.json',
33
+ },
34
+ 'text-block': {
35
+ status: 'active',
36
+ since: '0.2.0',
37
+ required_permission: null,
38
+ props_schema: 'nodes/text-block.schema.json',
39
+ },
40
+ },
41
+ };
42
+ /** Look up an entry by component id (returns undefined if absent). */
43
+ export function getComponentEntry(id, registry = COMPONENT_REGISTRY) {
44
+ return registry.components[id];
45
+ }
46
+ /**
47
+ * True if a component id may be selected (emitter) or rendered (renderer):
48
+ * it must exist and be `active` or `deprecated`. `removed` and unknown ids
49
+ * fail closed.
50
+ */
51
+ export function isApprovedComponent(id, registry = COMPONENT_REGISTRY) {
52
+ const entry = registry.components[id];
53
+ return !!entry && (entry.status === 'active' || entry.status === 'deprecated');
54
+ }
55
+ /** True if an approved component is deprecated (still renders, with a warning). */
56
+ export function isDeprecatedComponent(id, registry = COMPONENT_REGISTRY) {
57
+ return registry.components[id]?.status === 'deprecated';
58
+ }
59
+ //# sourceMappingURL=component-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-registry.js","sourceRoot":"","sources":["../../src/registry/component-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAuBH,oFAAoF;AACpF,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,gBAAgB,EAAE,OAAO;IACzB,UAAU,EAAE,sBAAsB;IAClC,UAAU,EAAE;QACV,WAAW,EAAE;YACX,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,OAAO;YACd,mBAAmB,EAAE,IAAI;YACzB,YAAY,EAAE,6BAA6B;SAC5C;QACD,YAAY,EAAE;YACZ,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,OAAO;YACd,mBAAmB,EAAE,IAAI;YACzB,YAAY,EAAE,8BAA8B;SAC7C;QACD,gBAAgB,EAAE;YAChB,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,OAAO;YACd,mBAAmB,EAAE,IAAI;YACzB,YAAY,EAAE,kCAAkC;SACjD;QACD,YAAY,EAAE;YACZ,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,OAAO;YACd,mBAAmB,EAAE,IAAI;YACzB,YAAY,EAAE,8BAA8B;SAC7C;KACF;CACF,CAAC;AAEF,sEAAsE;AACtE,MAAM,UAAU,iBAAiB,CAC/B,EAAU,EACV,WAA8B,kBAAkB;IAEhD,OAAO,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,EAAU,EACV,WAA8B,kBAAkB;IAEhD,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACtC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC;AACjF,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,qBAAqB,CACnC,EAAU,EACV,WAA8B,kBAAkB;IAEhD,OAAO,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,YAAY,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * CanvasStorageClient — the thin write client over canvas-service's record/Parts/lifecycle API.
3
+ *
4
+ * Types + HTTP wrapper ONLY (the locked SDK rule): no rendering, no business logic, no lifecycle
5
+ * enforcement, no caching, no auth minting. canvas-service owns persistence, the lifecycle state machine,
6
+ * and two-gate auth; this client only TRANSPORTS the write, forwarding the verified caller's bearer (+
7
+ * tenant/correlation) via `headers()` — it never mints a token. A stale-revision write surfaces as a typed
8
+ * conflict. This is the browser/edge transport of the one wire contract (the Go Conductor write-back is the
9
+ * other transport; see docs/design/canvas-storage-sdk.md). Append-only (v1): no in-place part Update.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+ import type { CanvasRecord, CanvasPart, CanvasAggregate, CanvasHome, PartInput, BatchAppendRequest, BatchAppendResult } from './types.js';
14
+ export interface CanvasStorageClientOptions {
15
+ /** Base URL the canvas-service record API is reached at (record routes live under /api/v1/canvases). */
16
+ baseUrl: string;
17
+ /** Optional fetch implementation (defaults to global fetch). */
18
+ fetchImpl?: typeof fetch;
19
+ /**
20
+ * Per-request headers the caller supplies — the verified-actor bearer (Authorization), the claimed
21
+ * tenant (X-Tenant-ID), and a correlation id (X-Correlation-ID). The client FORWARDS these; it never
22
+ * mints, refreshes, or inspects a token. canvas-service re-proves Gate-1 + Gate-2 from them.
23
+ */
24
+ headers?: () => Record<string, string> | Promise<Record<string, string>>;
25
+ }
26
+ /** A non-2xx response from canvas-service (404 = the owner/tenant collapse; 401 = auth). */
27
+ export declare class CanvasStorageError extends Error {
28
+ readonly status?: number | undefined;
29
+ readonly details?: unknown | undefined;
30
+ constructor(message: string, status?: number | undefined, details?: unknown | undefined);
31
+ }
32
+ /** A stale-write conflict (HTTP 409): the canvas `revision` changed since it was observed. */
33
+ export declare class CanvasStorageConflictError extends CanvasStorageError {
34
+ readonly currentRevision?: number | undefined;
35
+ constructor(message: string, currentRevision?: number | undefined, details?: unknown);
36
+ }
37
+ export declare class CanvasStorageClient {
38
+ private readonly baseUrl;
39
+ private readonly fetchImpl;
40
+ private readonly headers?;
41
+ constructor(opts: CanvasStorageClientOptions);
42
+ /** POST /api/v1/canvases — create a DRAFT (or resume the owner's open draft). */
43
+ create(): Promise<CanvasRecord>;
44
+ /** POST /api/v1/canvases/:id/name — DRAFT→ACTIVE birth, or rename an ACTIVE canvas. */
45
+ name(id: string, name: string): Promise<CanvasRecord>;
46
+ /** GET /api/v1/canvases/:id — open/read a canvas with its ordered parts. */
47
+ open(id: string): Promise<CanvasAggregate>;
48
+ /** GET /api/v1/canvases — the caller's ACTIVE canvases, recency-ordered. */
49
+ list(): Promise<CanvasRecord[]>;
50
+ /** GET /api/v1/canvases/home — the Workspace-Home read model in one read. */
51
+ home(): Promise<CanvasHome>;
52
+ /** POST /api/v1/canvases/:id/archive — ACTIVE→ARCHIVED (idempotent). */
53
+ archive(id: string): Promise<CanvasRecord>;
54
+ /** POST /api/v1/canvases/:id/restore — ARCHIVED→ACTIVE (idempotent). */
55
+ restore(id: string): Promise<CanvasRecord>;
56
+ /** POST /api/v1/canvases/:id/parts — append one authored part. */
57
+ addPart(id: string, part: PartInput): Promise<CanvasPart>;
58
+ /** DELETE /api/v1/canvases/:id/parts/:partId — hard-delete a part (append-only model; no Update). */
59
+ removePart(id: string, partId: string): Promise<void>;
60
+ /** POST /api/v1/canvases/:id/parts/reorder — rewrite the part order to the supplied id list. */
61
+ reorderParts(id: string, partIds: string[]): Promise<CanvasPart[]>;
62
+ /**
63
+ * POST /api/v1/canvases/:id/parts/apply — the Phase-5 write-back batch: append a batch of parts
64
+ * atomically, idempotent under the key, optimistic against `expectedRevision`. The SDK banks this
65
+ * contract now; the canvas-service route + durable dedup + revision check land in Phase 5.
66
+ */
67
+ apply(id: string, batch: BatchAppendRequest): Promise<BatchAppendResult>;
68
+ private request;
69
+ }
70
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/storage/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,eAAe,EACf,UAAU,EACV,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,0BAA0B;IACzC,wGAAwG;IACxG,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1E;AAED,4FAA4F;AAC5F,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,MAAM,CAAC,EAAE,MAAM;aACf,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,OAAO,CAAC,EAAE,OAAO,YAAA;CAKpC;AAED,8FAA8F;AAC9F,qBAAa,0BAA2B,SAAQ,kBAAkB;aAG9C,eAAe,CAAC,EAAE,MAAM;gBADxC,OAAO,EAAE,MAAM,EACC,eAAe,CAAC,EAAE,MAAM,YAAA,EACxC,OAAO,CAAC,EAAE,OAAO;CAKpB;AAID,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAwC;gBAErD,IAAI,EAAE,0BAA0B;IAM5C,iFAAiF;IACjF,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IAI/B,uFAAuF;IACvF,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAIrD,4EAA4E;IAC5E,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAI1C,4EAA4E;IAC5E,IAAI,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI/B,6EAA6E;IAC7E,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAI3B,wEAAwE;IACxE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI1C,wEAAwE;IACxE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI1C,kEAAkE;IAClE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAIzD,qGAAqG;IACrG,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD,gGAAgG;IAChG,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAMlE;;;;OAIG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAW1D,OAAO;CA4BtB"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * CanvasStorageClient — the thin write client over canvas-service's record/Parts/lifecycle API.
3
+ *
4
+ * Types + HTTP wrapper ONLY (the locked SDK rule): no rendering, no business logic, no lifecycle
5
+ * enforcement, no caching, no auth minting. canvas-service owns persistence, the lifecycle state machine,
6
+ * and two-gate auth; this client only TRANSPORTS the write, forwarding the verified caller's bearer (+
7
+ * tenant/correlation) via `headers()` — it never mints a token. A stale-revision write surfaces as a typed
8
+ * conflict. This is the browser/edge transport of the one wire contract (the Go Conductor write-back is the
9
+ * other transport; see docs/design/canvas-storage-sdk.md). Append-only (v1): no in-place part Update.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+ /** A non-2xx response from canvas-service (404 = the owner/tenant collapse; 401 = auth). */
14
+ export class CanvasStorageError extends Error {
15
+ constructor(message, status, details) {
16
+ super(message);
17
+ this.status = status;
18
+ this.details = details;
19
+ this.name = 'CanvasStorageError';
20
+ }
21
+ }
22
+ /** A stale-write conflict (HTTP 409): the canvas `revision` changed since it was observed. */
23
+ export class CanvasStorageConflictError extends CanvasStorageError {
24
+ constructor(message, currentRevision, details) {
25
+ super(message, 409, details);
26
+ this.currentRevision = currentRevision;
27
+ this.name = 'CanvasStorageConflictError';
28
+ }
29
+ }
30
+ const RECORD_BASE = '/api/v1/canvases';
31
+ export class CanvasStorageClient {
32
+ constructor(opts) {
33
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, '');
34
+ this.fetchImpl = opts.fetchImpl ?? fetch;
35
+ this.headers = opts.headers;
36
+ }
37
+ /** POST /api/v1/canvases — create a DRAFT (or resume the owner's open draft). */
38
+ create() {
39
+ return this.request('POST', '').then((d) => d.canvas);
40
+ }
41
+ /** POST /api/v1/canvases/:id/name — DRAFT→ACTIVE birth, or rename an ACTIVE canvas. */
42
+ name(id, name) {
43
+ return this.request('POST', `/${enc(id)}/name`, { name }).then((d) => d.canvas);
44
+ }
45
+ /** GET /api/v1/canvases/:id — open/read a canvas with its ordered parts. */
46
+ open(id) {
47
+ return this.request('GET', `/${enc(id)}`);
48
+ }
49
+ /** GET /api/v1/canvases — the caller's ACTIVE canvases, recency-ordered. */
50
+ list() {
51
+ return this.request('GET', '').then((d) => d.canvases);
52
+ }
53
+ /** GET /api/v1/canvases/home — the Workspace-Home read model in one read. */
54
+ home() {
55
+ return this.request('GET', '/home');
56
+ }
57
+ /** POST /api/v1/canvases/:id/archive — ACTIVE→ARCHIVED (idempotent). */
58
+ archive(id) {
59
+ return this.request('POST', `/${enc(id)}/archive`).then((d) => d.canvas);
60
+ }
61
+ /** POST /api/v1/canvases/:id/restore — ARCHIVED→ACTIVE (idempotent). */
62
+ restore(id) {
63
+ return this.request('POST', `/${enc(id)}/restore`).then((d) => d.canvas);
64
+ }
65
+ /** POST /api/v1/canvases/:id/parts — append one authored part. */
66
+ addPart(id, part) {
67
+ return this.request('POST', `/${enc(id)}/parts`, toPartBody(part)).then((d) => d.part);
68
+ }
69
+ /** DELETE /api/v1/canvases/:id/parts/:partId — hard-delete a part (append-only model; no Update). */
70
+ removePart(id, partId) {
71
+ return this.request('DELETE', `/${enc(id)}/parts/${enc(partId)}`);
72
+ }
73
+ /** POST /api/v1/canvases/:id/parts/reorder — rewrite the part order to the supplied id list. */
74
+ reorderParts(id, partIds) {
75
+ return this.request('POST', `/${enc(id)}/parts/reorder`, { part_ids: partIds }).then((d) => d.parts);
76
+ }
77
+ /**
78
+ * POST /api/v1/canvases/:id/parts/apply — the Phase-5 write-back batch: append a batch of parts
79
+ * atomically, idempotent under the key, optimistic against `expectedRevision`. The SDK banks this
80
+ * contract now; the canvas-service route + durable dedup + revision check land in Phase 5.
81
+ */
82
+ apply(id, batch) {
83
+ const body = {
84
+ idempotency_key: { turn_id: batch.idempotencyKey.turn_id, tool_call_id: batch.idempotencyKey.tool_call_id },
85
+ parts: batch.parts.map(toPartBody),
86
+ };
87
+ if (batch.expectedRevision !== undefined)
88
+ body.expected_revision = batch.expectedRevision;
89
+ return this.request('POST', `/${enc(id)}/parts/apply`, body);
90
+ }
91
+ // --- transport ---
92
+ async request(method, path, body) {
93
+ const provided = this.headers ? await this.headers() : {};
94
+ const headers = { ...provided };
95
+ if (body !== undefined)
96
+ headers['Content-Type'] = 'application/json';
97
+ const res = await this.fetchImpl(`${this.baseUrl}${RECORD_BASE}${path}`, {
98
+ method,
99
+ headers,
100
+ body: body !== undefined ? JSON.stringify(body) : undefined,
101
+ });
102
+ if (res.status === 204)
103
+ return undefined;
104
+ if (!res.ok) {
105
+ const detail = await safeJson(res);
106
+ if (res.status === 409) {
107
+ throw new CanvasStorageConflictError(`canvas-storage: stale-write conflict on ${method} ${path}`, revisionOf(detail), detail);
108
+ }
109
+ throw new CanvasStorageError(`canvas-storage: ${method} ${path} returned ${res.status}`, res.status, detail);
110
+ }
111
+ const envelope = (await res.json());
112
+ return envelope.data;
113
+ }
114
+ }
115
+ /** Encode a path segment (the ids are UUIDs, but encode defensively). */
116
+ function enc(seg) {
117
+ return encodeURIComponent(seg);
118
+ }
119
+ /** Map a PartInput to the canvas-service wire body (snake_case, nulls preserved). */
120
+ function toPartBody(p) {
121
+ return {
122
+ part_type: p.part_type,
123
+ text_content: p.text_content ?? null,
124
+ layout: p.layout ?? {},
125
+ metadata: p.metadata ?? {},
126
+ };
127
+ }
128
+ async function safeJson(res) {
129
+ try {
130
+ return await res.json();
131
+ }
132
+ catch {
133
+ return undefined;
134
+ }
135
+ }
136
+ /** Pull a `revision` (or `current_revision`) out of an error body, if present. */
137
+ function revisionOf(detail) {
138
+ if (detail && typeof detail === 'object') {
139
+ const d = detail;
140
+ const r = d.current_revision ?? d.revision;
141
+ if (typeof r === 'number')
142
+ return r;
143
+ }
144
+ return undefined;
145
+ }
146
+ //# sourceMappingURL=client.js.map