@saacms/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +25 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/access/index.d.ts +37 -0
  4. package/dist/access/index.d.ts.map +1 -0
  5. package/dist/access/index.js +6 -0
  6. package/dist/auth/index.d.ts +30 -0
  7. package/dist/auth/index.d.ts.map +1 -0
  8. package/dist/codegen/content-migration.d.ts +167 -0
  9. package/dist/codegen/content-migration.d.ts.map +1 -0
  10. package/dist/codegen/filter-openapi-for-user.d.ts +100 -0
  11. package/dist/codegen/filter-openapi-for-user.d.ts.map +1 -0
  12. package/dist/codegen/index.d.ts +19 -0
  13. package/dist/codegen/index.d.ts.map +1 -0
  14. package/dist/codegen/index.js +43 -0
  15. package/dist/codegen/openapi-types.d.ts +125 -0
  16. package/dist/codegen/openapi-types.d.ts.map +1 -0
  17. package/dist/codegen/to-d1-migration.d.ts +88 -0
  18. package/dist/codegen/to-d1-migration.d.ts.map +1 -0
  19. package/dist/codegen/to-drizzle.d.ts +131 -0
  20. package/dist/codegen/to-drizzle.d.ts.map +1 -0
  21. package/dist/codegen/to-openapi.d.ts +80 -0
  22. package/dist/codegen/to-openapi.d.ts.map +1 -0
  23. package/dist/codegen/to-puck-fields.d.ts +109 -0
  24. package/dist/codegen/to-puck-fields.d.ts.map +1 -0
  25. package/dist/codegen/to-ts-types.d.ts +59 -0
  26. package/dist/codegen/to-ts-types.d.ts.map +1 -0
  27. package/dist/hooks/index.d.ts +94 -0
  28. package/dist/hooks/index.d.ts.map +1 -0
  29. package/dist/hooks/index.js +8 -0
  30. package/dist/host/index.d.ts +109 -0
  31. package/dist/host/index.d.ts.map +1 -0
  32. package/dist/host/index.js +16 -0
  33. package/dist/index-172n82sz.js +4 -0
  34. package/dist/index-8g8ymd37.js +275 -0
  35. package/dist/index-a3pnt8yz.js +1494 -0
  36. package/dist/index-b59hfany.js +3078 -0
  37. package/dist/index-b7z43xwp.js +6 -0
  38. package/dist/index-r0at8zaw.js +13 -0
  39. package/dist/index-zgbq60fy.js +74 -0
  40. package/dist/index.d.ts +23 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +261 -0
  43. package/dist/observability/audit.d.ts +65 -0
  44. package/dist/observability/audit.d.ts.map +1 -0
  45. package/dist/observability/index.d.ts +2 -0
  46. package/dist/observability/index.d.ts.map +1 -0
  47. package/dist/publish/compile.d.ts +76 -0
  48. package/dist/publish/compile.d.ts.map +1 -0
  49. package/dist/runtime/auth-middleware.d.ts +34 -0
  50. package/dist/runtime/auth-middleware.d.ts.map +1 -0
  51. package/dist/runtime/boolean-columns.d.ts +65 -0
  52. package/dist/runtime/boolean-columns.d.ts.map +1 -0
  53. package/dist/runtime/cache.d.ts +62 -0
  54. package/dist/runtime/cache.d.ts.map +1 -0
  55. package/dist/runtime/create-route.d.ts +26 -0
  56. package/dist/runtime/create-route.d.ts.map +1 -0
  57. package/dist/runtime/delete-route.d.ts +15 -0
  58. package/dist/runtime/delete-route.d.ts.map +1 -0
  59. package/dist/runtime/drafts-route.d.ts +65 -0
  60. package/dist/runtime/drafts-route.d.ts.map +1 -0
  61. package/dist/runtime/health-route.d.ts +23 -0
  62. package/dist/runtime/health-route.d.ts.map +1 -0
  63. package/dist/runtime/index.d.ts +24 -0
  64. package/dist/runtime/index.d.ts.map +1 -0
  65. package/dist/runtime/index.js +17 -0
  66. package/dist/runtime/json-columns.d.ts +50 -0
  67. package/dist/runtime/json-columns.d.ts.map +1 -0
  68. package/dist/runtime/list-route.d.ts +20 -0
  69. package/dist/runtime/list-route.d.ts.map +1 -0
  70. package/dist/runtime/openapi-route.d.ts +30 -0
  71. package/dist/runtime/openapi-route.d.ts.map +1 -0
  72. package/dist/runtime/pattern-route.d.ts +48 -0
  73. package/dist/runtime/pattern-route.d.ts.map +1 -0
  74. package/dist/runtime/problem-details.d.ts +32 -0
  75. package/dist/runtime/problem-details.d.ts.map +1 -0
  76. package/dist/runtime/put-route.d.ts +19 -0
  77. package/dist/runtime/put-route.d.ts.map +1 -0
  78. package/dist/runtime/read-route.d.ts +26 -0
  79. package/dist/runtime/read-route.d.ts.map +1 -0
  80. package/dist/runtime/scale-cost.d.ts +84 -0
  81. package/dist/runtime/scale-cost.d.ts.map +1 -0
  82. package/dist/runtime/scheme-route.d.ts +49 -0
  83. package/dist/runtime/scheme-route.d.ts.map +1 -0
  84. package/dist/runtime/services.d.ts +42 -0
  85. package/dist/runtime/services.d.ts.map +1 -0
  86. package/dist/runtime/update-route.d.ts +20 -0
  87. package/dist/runtime/update-route.d.ts.map +1 -0
  88. package/dist/runtime/upload-route.d.ts +46 -0
  89. package/dist/runtime/upload-route.d.ts.map +1 -0
  90. package/dist/schema/index.d.ts +185 -0
  91. package/dist/schema/index.d.ts.map +1 -0
  92. package/dist/schema/index.js +40 -0
  93. package/dist/schema/media.d.ts +237 -0
  94. package/dist/schema/media.d.ts.map +1 -0
  95. package/dist/schema/plugin-trust.d.ts +144 -0
  96. package/dist/schema/plugin-trust.d.ts.map +1 -0
  97. package/dist/signals/index.d.ts +10 -0
  98. package/dist/signals/index.d.ts.map +1 -0
  99. package/dist/signals/index.js +10 -0
  100. package/dist/storage/index.d.ts +120 -0
  101. package/dist/storage/index.d.ts.map +1 -0
  102. package/dist/storage/index.js +13 -0
  103. package/dist/tenant/index.d.ts +105 -0
  104. package/dist/tenant/index.d.ts.map +1 -0
  105. package/dist/theme/index.d.ts +56 -0
  106. package/dist/theme/index.d.ts.map +1 -0
  107. package/dist/types/ids.d.ts +45 -0
  108. package/dist/types/ids.d.ts.map +1 -0
  109. package/dist/types/ids.js +15 -0
  110. package/dist/types/index.d.ts +3 -0
  111. package/dist/types/index.d.ts.map +1 -0
  112. package/dist/types/index.js +6 -0
  113. package/dist/types/user.d.ts +14 -0
  114. package/dist/types/user.d.ts.map +1 -0
  115. package/dist/types/user.js +1 -0
  116. package/package.json +116 -0
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Plugin trust & capability model — ADR 0029 (refines ADR 0027 §6's `DEFERRED`
3
+ * capability-scoping sub-decision; ADR 0027 stays, ADR 0029 supersedes only that
4
+ * row).
5
+ *
6
+ * v1 trust reality (restated from ADR 0027 §6, honestly): plugins are FULLY
7
+ * TRUSTED. `definePlugin` packages run in-process with the runtime; the npm
8
+ * install step already runs arbitrary code. There is no sandbox here and this
9
+ * module does NOT add one. Capability *enforcement* is therefore NOT a v1
10
+ * security boundary — it stays `DEFERRED` (the v2 Effect-services capability
11
+ * redesign ADR 0027 names).
12
+ *
13
+ * What this module DOES add is the cheap, in-process, convenient→secure move
14
+ * (ADR 0026 Load-Bearing Rule; SRS §Ch.4 "make the insecure path noisier",
15
+ * §Ch.5 least privilege): a plugin may *declare* the capabilities it intends to
16
+ * use, and `assertPluginCapabilities` *detects* — purely, deterministically, no
17
+ * I/O — when a plugin's actual contributed surface exceeds its declaration. This
18
+ * is the exact discipline of `assertTenantIsolation` (ADR 0026 §O9 / ADR 0027
19
+ * §8): an additive, opt-in declare+assert affordance that makes the trap loud
20
+ * and cheap to avoid without changing any existing plugin/runtime behaviour.
21
+ *
22
+ * Detection ≠ enforcement. A declared-but-exceeded capability is *reported*, not
23
+ * *blocked*. The value is provenance/intent transparency, a supply-chain review
24
+ * surface, and the on-ramp to v2 enforcement — not a runtime sandbox.
25
+ *
26
+ * `capabilities` is an OPTIONAL field on `PluginDef`; an existing plugin with no
27
+ * declaration keeps byte-identical behaviour and is merely *listed* as
28
+ * undeclared (CI chooses strictness — see `strict`).
29
+ */
30
+ import type { PluginDef, SaacmsConfig } from "./index.ts";
31
+ /**
32
+ * The closed set of capability tags a plugin may declare. Each tag is justified
33
+ * by a real `PluginDef` contribution surface (ADR 0029 §Capability Vocabulary):
34
+ *
35
+ * - `collections` — registers Collections (schema + generated CRUD surface).
36
+ * - `blocks` — registers Blocks (editor/render surface).
37
+ * - `pages` — registers Pages and/or Page Templates (route surface).
38
+ * - `routes` — mounts custom HTTP routes under the plugin namespace.
39
+ * - `hooks` — registers lifecycle hooks on Collection moments.
40
+ * - `services` — publishes Effect service factories into the runtime.
41
+ * - `storage:read` — reads stored rows (implied by contributing Collections).
42
+ * - `storage:write` — writes stored rows (implied by Collections and/or an
43
+ * opaque one-shot `init` install effect).
44
+ * - `admin-pages` — registers admin UI pages / menu entries.
45
+ * - `migrations` — ships db and/or content migrations.
46
+ *
47
+ * Closed by construction so CI exhaustiveness checks and v2 enforcement have a
48
+ * finite, auditable surface.
49
+ */
50
+ export type PluginCapability = "collections" | "blocks" | "pages" | "routes" | "hooks" | "services" | "storage:read" | "storage:write" | "admin-pages" | "migrations";
51
+ /**
52
+ * The closed vocabulary as a value, ordered. Exported so CI / tests can assert
53
+ * exhaustiveness against the `PluginCapability` union.
54
+ */
55
+ export declare const PLUGIN_CAPABILITIES: ReadonlyArray<PluginCapability>;
56
+ /**
57
+ * Derive the capabilities a plugin ACTUALLY contributes from its `PluginDef`
58
+ * shape alone — pure, structural, no runtime introspection, no I/O.
59
+ *
60
+ * Derivation is *presence-based*: we read which `PluginDef` categories the
61
+ * plugin populates, not what the code inside them does (the bodies of
62
+ * `routes` / `hooks` / `services` / `init` are opaque `unknown` and cannot be
63
+ * inspected without the v2 runtime redesign — see ADR 0029 §Honest Limits).
64
+ * This boundary is exactly what is derivable WITHOUT a contract change, so the
65
+ * detector is well-defined and not BLOCKED.
66
+ *
67
+ * Mapping (ADR 0029 §Surface Derivation):
68
+ * - non-empty `collections` → `collections`, `storage:read`, `storage:write`
69
+ * - non-empty `blocks` → `blocks`
70
+ * - non-empty `pages` → `pages`
71
+ * - non-empty `pageTemplates`→ `pages`
72
+ * - non-empty `routes` → `routes`
73
+ * - non-empty `hooks` → `hooks`
74
+ * - non-empty `services` → `services`
75
+ * - `admin.pages`/`admin.menu` present → `admin-pages`
76
+ * - `migrations.db`/`.content` present → `migrations`
77
+ * - `init` present (opaque effect) → `storage:write`
78
+ * (conservative/fail-closed: an opaque install effect cannot be proven inert,
79
+ * so it is attributed the broadest data-affecting tag; declaring it is the
80
+ * cheap secure move — ADR 0026 Load-Bearing Rule.)
81
+ */
82
+ export declare function deriveContributedCapabilities(plugin: PluginDef): ReadonlyArray<PluginCapability>;
83
+ export interface AssertPluginCapabilitiesOpts {
84
+ /**
85
+ * Strict mode (fail-closed): when `true`, a plugin that contributes any
86
+ * surface but declares NO `capabilities` at all also fails the check
87
+ * (`ok: false`).
88
+ *
89
+ * Default `false` for additive back-compat: existing plugins/configs with no
90
+ * `capabilities` declaration keep byte-identical behaviour — they are merely
91
+ * *listed* in `undeclared` and `ok` is unaffected by them. CI opts into the
92
+ * convenient→secure posture with `{ strict: true }`.
93
+ */
94
+ readonly strict?: boolean;
95
+ }
96
+ /**
97
+ * A single declared-capability escalation: `plugin` contributes `used` but did
98
+ * not list it among its declared `capabilities`. `declared` is always `false`
99
+ * for a finding (a finding *is* the absence) — it is kept on the shape so the
100
+ * record is self-describing for CI logs and future severity tiers.
101
+ */
102
+ export interface PluginCapabilityFinding {
103
+ readonly plugin: string;
104
+ readonly used: PluginCapability;
105
+ readonly declared: boolean;
106
+ }
107
+ export interface PluginCapabilityReport {
108
+ /**
109
+ * `true` when no plugin's contributed surface exceeds its declared
110
+ * `capabilities`. In `strict` mode, also requires every surface-contributing
111
+ * plugin to have declared a `capabilities` array.
112
+ */
113
+ readonly ok: boolean;
114
+ /** Declared-but-exceeded escalations. Empty when no declared plugin over-reaches. */
115
+ readonly violations: ReadonlyArray<PluginCapabilityFinding>;
116
+ /**
117
+ * Names of plugins that contributed surface but declared NO `capabilities`.
118
+ * Reported regardless of `strict` so CI can choose its own policy; only
119
+ * affects `ok` when `strict` is `true`.
120
+ */
121
+ readonly undeclared: ReadonlyArray<string>;
122
+ }
123
+ /**
124
+ * Pure, deterministic detector. For each plugin in `config.plugins`, derive the
125
+ * actual contributed surface from its `PluginDef` and flag any contributed
126
+ * capability not present in its declared `capabilities`.
127
+ *
128
+ * - Plugin declares `capabilities`: each contributed capability NOT in the
129
+ * declared set → a `violation` (`ok: false`). Declaring exactly (or a
130
+ * superset of) its surface → passes.
131
+ * - Plugin declares NO `capabilities` (field absent): listed in `undeclared`.
132
+ * Non-strict (default) ⇒ does not affect `ok` (additive back-compat). Strict
133
+ * ⇒ contributes to `ok: false`.
134
+ *
135
+ * No I/O, no mutation of inputs, constant-ish time over the plugin set.
136
+ * Integrate into CI exactly like `assertTenantIsolation`:
137
+ *
138
+ * ```typescript
139
+ * const r = assertPluginCapabilities(config, { strict: true })
140
+ * if (!r.ok) throw new Error(`Plugin capability escalation: ${JSON.stringify(r)}`)
141
+ * ```
142
+ */
143
+ export declare function assertPluginCapabilities(config: Pick<SaacmsConfig, "plugins">, opts?: AssertPluginCapabilitiesOpts): PluginCapabilityReport;
144
+ //# sourceMappingURL=plugin-trust.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-trust.d.ts","sourceRoot":"","sources":["../../src/schema/plugin-trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAMzD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,OAAO,GACP,UAAU,GACV,cAAc,GACd,eAAe,GACf,aAAa,GACb,YAAY,CAAA;AAEhB;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,gBAAgB,CAWtD,CAAA;AAMV;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,SAAS,GAChB,aAAa,CAAC,gBAAgB,CAAC,CAgCjC;AAMD,MAAM,WAAW,4BAA4B;IAC3C;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,qFAAqF;IACrF,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAA;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAC3C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,EACrC,IAAI,GAAE,4BAAiC,GACtC,sBAAsB,CAyBxB"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Cross-framework reactive primitive per ADR 0023 Part A.
3
+ *
4
+ * `@preact/signals-core` is the v1 polyfill for the TC39 Signals proposal.
5
+ * Block authors importing `signal` / `computed` / `effect` from `@saacms/core`
6
+ * get a primitive that works inside Web Component Blocks today and will swap to
7
+ * native `globalThis.Signal` once browsers ship it without any Block code change.
8
+ */
9
+ export { signal, computed, effect } from "@preact/signals-core";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signals/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA"}
@@ -0,0 +1,10 @@
1
+ import {
2
+ computed,
3
+ effect,
4
+ signal
5
+ } from "../index-172n82sz.js";
6
+ export {
7
+ signal,
8
+ effect,
9
+ computed
10
+ };
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Storage adapter interface per ADR 0009 (Media as a Collection kind).
3
+ *
4
+ * Implementations (`@saacms/storage-r2`, `@saacms/storage-vercel-blob`,
5
+ * `@saacms/storage-s3`) provide the binary-bucket primitives used by Media
6
+ * Collections in `bucket` storage mode. `repo`-mode collections do not go
7
+ * through a StorageAdapter — they're committed to the host's static-asset folder
8
+ * on Publish.
9
+ *
10
+ * The interface uses Web standard `Request` / `Response` shapes so the same
11
+ * adapter runs unchanged on Vercel Edge, Cloudflare Workers, Bun, and Node.
12
+ */
13
+ /**
14
+ * Per-`put` options surfaced to the adapter.
15
+ * `contentType` and `cacheControl` map directly to the corresponding upstream
16
+ * object metadata (R2 / Blob / S3 all expose them).
17
+ */
18
+ export interface StoragePutOptions {
19
+ readonly contentType?: string;
20
+ readonly cacheControl?: string;
21
+ readonly contentDisposition?: string;
22
+ readonly metadata?: Readonly<Record<string, string>>;
23
+ }
24
+ export interface StoragePutResult {
25
+ readonly key: string;
26
+ readonly etag?: string;
27
+ readonly size?: number;
28
+ }
29
+ /**
30
+ * Body shapes accepted by `put`. Matches the Web `BodyInit` set so callers can
31
+ * stream a `ReadableStream`, hand off a `Blob`, or pass a buffer.
32
+ */
33
+ export type StoragePutBody = ReadableStream<Uint8Array> | Blob | ArrayBuffer | ArrayBufferView | string;
34
+ /**
35
+ * Options for generating a signed URL — adapters that don't support a given
36
+ * verb may return a non-functional URL or throw at `signedUrl` time.
37
+ */
38
+ export interface SignedUrlOptions {
39
+ readonly method?: "GET" | "PUT" | "DELETE";
40
+ /** Seconds until the signed URL expires. */
41
+ readonly expiresIn?: number;
42
+ readonly contentType?: string;
43
+ }
44
+ export interface StorageAdapter {
45
+ readonly name: string;
46
+ /** Upload an object. Returns the canonical key the adapter chose to store it under. */
47
+ put(key: string, body: StoragePutBody, opts?: StoragePutOptions): Promise<StoragePutResult>;
48
+ /**
49
+ * Fetch an object as a Web `Response`. Caller may stream the body, read it
50
+ * as an `ArrayBuffer`, or proxy it directly back to the user.
51
+ */
52
+ get(key: string): Promise<Response>;
53
+ /** Remove an object. Idempotent — removing a missing key is not an error. */
54
+ delete(key: string): Promise<void>;
55
+ /** Pre-signed URL for direct client → bucket transfer. */
56
+ signedUrl(key: string, opts?: SignedUrlOptions): Promise<string>;
57
+ }
58
+ /**
59
+ * Cursor-based pagination + filter options for `list`.
60
+ * `where` is a column→value AND-conjoined predicate set; richer expressions
61
+ * land in v1.x. `cursor` is opaque to the caller and adapter-defined.
62
+ */
63
+ export interface ListOpts {
64
+ readonly limit?: number;
65
+ readonly cursor?: string;
66
+ readonly where?: Readonly<Record<string, unknown>>;
67
+ readonly orderBy?: ReadonlyArray<{
68
+ readonly col: string;
69
+ readonly dir: "asc" | "desc";
70
+ }>;
71
+ }
72
+ export interface ListResult<T> {
73
+ readonly rows: ReadonlyArray<T>;
74
+ readonly nextCursor: string | null;
75
+ }
76
+ /**
77
+ * Tagged error for row-storage operations. Bubbles up to the runtime which
78
+ * converts to RFC 9457 Problem Details (ADR 0018).
79
+ */
80
+ export declare class RowStorageError extends Error {
81
+ readonly code: "NOT_FOUND" | "INSERT_FAILED" | "UPDATE_FAILED" | "DELETE_FAILED" | "QUERY_FAILED" | "NOT_IMPLEMENTED" | "MISCONFIGURED";
82
+ readonly cause?: unknown | undefined;
83
+ readonly name = "RowStorageError";
84
+ constructor(code: "NOT_FOUND" | "INSERT_FAILED" | "UPDATE_FAILED" | "DELETE_FAILED" | "QUERY_FAILED" | "NOT_IMPLEMENTED" | "MISCONFIGURED", message: string, cause?: unknown | undefined);
85
+ }
86
+ /**
87
+ * Per ADR 0030 + ADR 0020 — thrown by the storage adapter (the anti-corruption
88
+ * boundary) when a DB unique-constraint violation is detected. Routes check
89
+ * `instanceof UniqueConstraintError` to map it to a 409 response; no route
90
+ * ever inspects raw DB error messages.
91
+ */
92
+ export declare class UniqueConstraintError extends Error {
93
+ readonly table: string;
94
+ readonly name = "UniqueConstraintError";
95
+ constructor(table: string, cause?: unknown);
96
+ }
97
+ /**
98
+ * Row-storage adapter — the Collection-record equivalent of `StorageAdapter`.
99
+ * Implementations: `@saacms/storage-d1` (v1.0); postgres / sqlite-local follow.
100
+ *
101
+ * Per ADR 0020 — the Storage bounded context fronts both binary (`StorageAdapter`)
102
+ * and tabular (`RowStorageAdapter`) backing stores.
103
+ */
104
+ export interface RowStorageAdapter {
105
+ getById<T>(table: string, id: string): Promise<T | null>;
106
+ list<T>(table: string, opts?: ListOpts): Promise<ListResult<T>>;
107
+ insert<T>(table: string, row: T): Promise<{
108
+ id: string;
109
+ }>;
110
+ update<T>(table: string, id: string, patch: Partial<T>): Promise<void>;
111
+ delete(table: string, id: string): Promise<void>;
112
+ }
113
+ /** The shape `SaacmsConfig.storage` accepts. */
114
+ export interface SaacmsStorageConfig {
115
+ /** Row store for Collection records (D1 in v1.0). */
116
+ readonly rows?: RowStorageAdapter;
117
+ /** Binary blob store for Media Collections (R2 in v1.0). */
118
+ readonly media?: StorageAdapter;
119
+ }
120
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Storage adapter interface per ADR 0009 (Media as a Collection kind).
3
+ *
4
+ * Implementations (`@saacms/storage-r2`, `@saacms/storage-vercel-blob`,
5
+ * `@saacms/storage-s3`) provide the binary-bucket primitives used by Media
6
+ * Collections in `bucket` storage mode. `repo`-mode collections do not go
7
+ * through a StorageAdapter — they're committed to the host's static-asset folder
8
+ * on Publish.
9
+ *
10
+ * The interface uses Web standard `Request` / `Response` shapes so the same
11
+ * adapter runs unchanged on Vercel Edge, Cloudflare Workers, Bun, and Node.
12
+ */
13
+ export {};
@@ -0,0 +1,105 @@
1
+ /**
2
+ * App multi-tenancy sugar — ADR 0015 + ADR 0026 §O9.
3
+ *
4
+ * ADR 0015 defers app multi-tenancy to user code via two ADR-0006 primitives:
5
+ * 1. A `read` predicate returning `{ where: { [recordField]: user.claims[userField] } }`
6
+ * scopes every list/read to the caller's tenant.
7
+ * 2. A `beforeChange` hook injecting `recordField = user.claims[userField]` on
8
+ * every create/update ensures tenant ownership is set at write time.
9
+ *
10
+ * This module provides ADR-0015-consistent SUGAR — NOT a new access grain (ADR
11
+ * 0006 is unchanged). `tenantScoped` produces the exact shape that
12
+ * `defineCollection({ access, hooks })` expects. `assertTenantIsolation` is a
13
+ * pure CI detector that surfaces Collections whose schema has a tenant field but
14
+ * lacks an `access.read` predicate, making the dangerous path loud and cheap to
15
+ * catch (SRS §Ch.4 "Design Tradeoffs": make the insecure path noisier).
16
+ *
17
+ * If `tenantScoped` or `assertTenantIsolation` cannot be added without modifying
18
+ * existing access/runtime/storage behaviour, that would be a BLOCKED finding.
19
+ * Neither touches existing route handlers, access evaluators, or storage adapters.
20
+ */
21
+ import type { AccessPredicate } from "../access/index.ts";
22
+ import type { HookFn } from "../hooks/index.ts";
23
+ import type { AnySchema, SaacmsConfig } from "../schema/index.ts";
24
+ export interface TenantScopedOpts {
25
+ /**
26
+ * Key in `user.claims` that holds the tenant identifier.
27
+ * E.g. `"tenantId"` reads `user.claims["tenantId"]`.
28
+ */
29
+ readonly userField: string;
30
+ /**
31
+ * Record field to scope on. Defaults to `userField` when omitted.
32
+ * E.g. `recordField: "tenantId"` produces `{ where: { tenantId: claimValue } }`.
33
+ */
34
+ readonly recordField?: string;
35
+ }
36
+ export interface TenantScopedResult {
37
+ /**
38
+ * Drop into `defineCollection({ access: { read: ts.read } })`.
39
+ *
40
+ * Returns `{ where: { [recordField]: user.claims[userField] } }` for
41
+ * authenticated callers (scopes list + per-record read to the caller's tenant).
42
+ * Returns `false` (deny) for anonymous callers — anonymous cannot read
43
+ * tenant-scoped data.
44
+ */
45
+ readonly read: AccessPredicate;
46
+ readonly hooks: {
47
+ /**
48
+ * Drop into `defineCollection({ hooks: { beforeChange: ts.hooks.beforeChange } })`.
49
+ *
50
+ * Injects `recordField = user.claims[userField]` into `ctx.data` before
51
+ * every create/update, ensuring tenant ownership is set at write time without
52
+ * requiring the caller to supply it.
53
+ */
54
+ readonly beforeChange: HookFn<AnySchema>;
55
+ };
56
+ }
57
+ /**
58
+ * ADR-0015-consistent sugar for app multi-tenancy. Returns the `{ read, hooks }`
59
+ * pair that drops directly into a `defineCollection`. No new access grain.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const ts = tenantScoped({ userField: "tenantId" })
64
+ * export const Orders = defineCollection({
65
+ * slug: "orders",
66
+ * schema: OrderSchema,
67
+ * access: { read: ts.read, create: ts.read, update: ts.read, delete: ts.read },
68
+ * hooks: { beforeChange: ts.hooks.beforeChange },
69
+ * })
70
+ * ```
71
+ */
72
+ export declare function tenantScoped(opts: TenantScopedOpts): TenantScopedResult;
73
+ export interface AssertTenantIsolationOpts {
74
+ /**
75
+ * The schema field name that carries the tenant identifier. Collections that
76
+ * have this field in their Effect Schema but lack an `access.read` predicate
77
+ * are reported as "unscoped" — they would expose data across tenants.
78
+ */
79
+ readonly tenantField: string;
80
+ /**
81
+ * Collection slugs exempt from the check (e.g. shared/public data).
82
+ */
83
+ readonly exempt?: ReadonlyArray<string>;
84
+ }
85
+ export interface TenantIsolationResult {
86
+ /** `true` when all tenant-capable collections have an `access.read` predicate. */
87
+ readonly ok: boolean;
88
+ /** Slugs of collections flagged as unscoped. Empty when `ok` is `true`. */
89
+ readonly unscoped: ReadonlyArray<string>;
90
+ }
91
+ /**
92
+ * Pure detector: inspects `config.collections` and returns any collection whose
93
+ * Effect Schema has a property named `opts.tenantField` but lacks an
94
+ * `access.read` predicate. Runs in constant time with no I/O.
95
+ *
96
+ * Integrate into CI:
97
+ * ```typescript
98
+ * const check = assertTenantIsolation(config, { tenantField: "tenantId" })
99
+ * if (!check.ok) {
100
+ * throw new Error(`Missing tenant predicate on: ${check.unscoped.join(", ")}`)
101
+ * }
102
+ * ```
103
+ */
104
+ export declare function assertTenantIsolation(config: SaacmsConfig, opts: AssertTenantIsolationOpts): TenantIsolationResult;
105
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tenant/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAMjE,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE;QACd;;;;;;WAMG;QACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;KACzC,CAAA;CACF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,CA0BvE;AAMD,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,qBAAqB;IACpC,kFAAkF;IAClF,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACzC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,yBAAyB,GAC9B,qBAAqB,CAevB"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Theme & Scheme primitive — ADR 0033.
3
+ *
4
+ * Theme = Developer-defined token contract (Structure / Zone-2).
5
+ * Scheme = Owner-operated named value set (Instance / Zone-1).
6
+ *
7
+ * `defineTheme` is the additive sibling of `defineCollection` / `defineBlock` /
8
+ * `definePlugin`. When absent from `SaacmsConfig` ⇒ zero behaviour change.
9
+ *
10
+ * `themeFingerprint` is the pure, deterministic cache-key contribution that
11
+ * composes with the access fingerprint at the host adapter's cache site,
12
+ * preventing the saastarter cross-tenant/cross-scheme CDN bleed (ADR 0033 §5).
13
+ */
14
+ /** One of the four presentation token groups. */
15
+ export type ThemeTokenGroup = "colour" | "typography" | "radius" | "motion";
16
+ /** Declaration of a single CSS custom-property slot in the theme contract. */
17
+ export interface ThemeTokenDef {
18
+ /** CSS custom-property name WITHOUT the leading `--`. E.g. `"color-primary"`. */
19
+ readonly name: string;
20
+ readonly group: ThemeTokenGroup;
21
+ /** When true, a Scheme may supply a `darkTokens[name]` override. */
22
+ readonly dark?: boolean;
23
+ readonly description?: string;
24
+ }
25
+ /**
26
+ * The Developer-authored token contract. Additive optional: when absent from
27
+ * `SaacmsConfig` no new behaviour is introduced and all existing routes and
28
+ * cache keys are byte-identical (ADR 0033 §8).
29
+ */
30
+ export interface ThemeDef {
31
+ readonly tokens: ReadonlyArray<ThemeTokenDef>;
32
+ }
33
+ /**
34
+ * Identity-function declaration — lets downstream code hold a typed reference
35
+ * and the codegen walk a canonical shape. Mirror of `defineCollection` et al.
36
+ */
37
+ export declare function defineTheme(opts: ThemeDef): ThemeDef;
38
+ /**
39
+ * Pure, deterministic cache-key contribution from the active theme context.
40
+ *
41
+ * Returns a stable opaque string that MUST be combined with the access
42
+ * fingerprint (e.g. `role:${role}`) to form a complete themed cache key:
43
+ *
44
+ * `const key = `role:${role}:${themeFingerprint(schemeId, darkMode, tenant)}`
45
+ *
46
+ * Properties guaranteed (invariant 18 in security-invariants.test.ts):
47
+ * - Deterministic: same inputs → same output, always.
48
+ * - Stable: no random or time-based components.
49
+ * - Composable: append to the access fingerprint, do not replace it.
50
+ * - Discriminating: distinct across schemes, dark/light, and tenants.
51
+ *
52
+ * When `schemeId` is null (no active scheme) the fingerprint is still defined
53
+ * and stable — it discriminates "no scheme" from any named scheme.
54
+ */
55
+ export declare function themeFingerprint(schemeId: string | null, darkMode: boolean, tenantScope?: string): string;
56
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,iDAAiD;AACjD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE3E,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;CAC9C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAEpD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,QAAQ,EAAE,OAAO,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAMR"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Branded ID types so the type system catches "I passed a UserID where a SessionID was expected".
3
+ * Per ADR 0020 (each aggregate has its own identity).
4
+ */
5
+ declare const __brand: unique symbol;
6
+ export type UserID = string & {
7
+ readonly [__brand]: "UserID";
8
+ };
9
+ export type SessionID = string & {
10
+ readonly [__brand]: "SessionID";
11
+ };
12
+ export type CollectionSlug = string & {
13
+ readonly [__brand]: "CollectionSlug";
14
+ };
15
+ export type RecordID = string & {
16
+ readonly [__brand]: "RecordID";
17
+ };
18
+ export type PageID = string & {
19
+ readonly [__brand]: "PageID";
20
+ };
21
+ export type BlockSlug = string & {
22
+ readonly [__brand]: "BlockSlug";
23
+ };
24
+ export type DraftID = string & {
25
+ readonly [__brand]: "DraftID";
26
+ };
27
+ export type PublicationID = string & {
28
+ readonly [__brand]: "PublicationID";
29
+ };
30
+ export type MigrationID = string & {
31
+ readonly [__brand]: "MigrationID";
32
+ };
33
+ export declare const id: {
34
+ user: (s: string) => UserID;
35
+ session: (s: string) => SessionID;
36
+ collection: (s: string) => CollectionSlug;
37
+ record: (s: string) => RecordID;
38
+ page: (s: string) => PageID;
39
+ block: (s: string) => BlockSlug;
40
+ draft: (s: string) => DraftID;
41
+ publication: (s: string) => PublicationID;
42
+ migration: (s: string) => MigrationID;
43
+ };
44
+ export {};
45
+ //# sourceMappingURL=ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/types/ids.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,CAAC,MAAM,OAAO,EAAE,OAAO,MAAM,CAAA;AAEpC,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAA;AAC9D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,WAAW,CAAA;CAAE,CAAA;AACpE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAA;AAC9E,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,UAAU,CAAA;CAAE,CAAA;AAClE,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAA;AAC9D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,WAAW,CAAA;CAAE,CAAA;AACpE,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;CAAE,CAAA;AAChE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,CAAA;AAC5E,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,aAAa,CAAA;CAAE,CAAA;AAExE,eAAO,MAAM,EAAE;cACH,MAAM,KAAU,MAAM;iBACnB,MAAM,KAAU,SAAS;oBACtB,MAAM,KAAU,cAAc;gBAClC,MAAM,KAAU,QAAQ;cAC1B,MAAM,KAAU,MAAM;eACrB,MAAM,KAAU,SAAS;eACzB,MAAM,KAAU,OAAO;qBACjB,MAAM,KAAU,aAAa;mBAC/B,MAAM,KAAU,WAAW;CAC3C,CAAA"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Branded ID types so the type system catches "I passed a UserID where a SessionID was expected".
3
+ * Per ADR 0020 (each aggregate has its own identity).
4
+ */
5
+ export const id = {
6
+ user: (s) => s,
7
+ session: (s) => s,
8
+ collection: (s) => s,
9
+ record: (s) => s,
10
+ page: (s) => s,
11
+ block: (s) => s,
12
+ draft: (s) => s,
13
+ publication: (s) => s,
14
+ migration: (s) => s,
15
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./ids.ts";
2
+ export * from "./user.ts";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA"}
@@ -0,0 +1,6 @@
1
+ import {
2
+ id
3
+ } from "../index-r0at8zaw.js";
4
+ export {
5
+ id
6
+ };
@@ -0,0 +1,14 @@
1
+ import type { UserID } from "./ids.ts";
2
+ /**
3
+ * The user projection passed to access predicates and hooks.
4
+ * Per ADR 0020 — this is NOT the Better Auth row; it's the Identity-context
5
+ * projection of it that the Content/Authoring/Publishing contexts may see.
6
+ */
7
+ export interface User {
8
+ readonly id: UserID;
9
+ readonly email: string | null;
10
+ readonly role: Role;
11
+ readonly claims: Readonly<Record<string, unknown>>;
12
+ }
13
+ export type Role = "admin" | "editor" | "viewer" | "user" | (string & {});
14
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/types/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEtC;;;;GAIG;AACH,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACnD;AAED,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};