@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.
- package/README.md +25 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/access/index.d.ts +37 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/index.js +6 -0
- package/dist/auth/index.d.ts +30 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/codegen/content-migration.d.ts +167 -0
- package/dist/codegen/content-migration.d.ts.map +1 -0
- package/dist/codegen/filter-openapi-for-user.d.ts +100 -0
- package/dist/codegen/filter-openapi-for-user.d.ts.map +1 -0
- package/dist/codegen/index.d.ts +19 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +43 -0
- package/dist/codegen/openapi-types.d.ts +125 -0
- package/dist/codegen/openapi-types.d.ts.map +1 -0
- package/dist/codegen/to-d1-migration.d.ts +88 -0
- package/dist/codegen/to-d1-migration.d.ts.map +1 -0
- package/dist/codegen/to-drizzle.d.ts +131 -0
- package/dist/codegen/to-drizzle.d.ts.map +1 -0
- package/dist/codegen/to-openapi.d.ts +80 -0
- package/dist/codegen/to-openapi.d.ts.map +1 -0
- package/dist/codegen/to-puck-fields.d.ts +109 -0
- package/dist/codegen/to-puck-fields.d.ts.map +1 -0
- package/dist/codegen/to-ts-types.d.ts +59 -0
- package/dist/codegen/to-ts-types.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +94 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +8 -0
- package/dist/host/index.d.ts +109 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +16 -0
- package/dist/index-172n82sz.js +4 -0
- package/dist/index-8g8ymd37.js +275 -0
- package/dist/index-a3pnt8yz.js +1494 -0
- package/dist/index-b59hfany.js +3078 -0
- package/dist/index-b7z43xwp.js +6 -0
- package/dist/index-r0at8zaw.js +13 -0
- package/dist/index-zgbq60fy.js +74 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +261 -0
- package/dist/observability/audit.d.ts +65 -0
- package/dist/observability/audit.d.ts.map +1 -0
- package/dist/observability/index.d.ts +2 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/publish/compile.d.ts +76 -0
- package/dist/publish/compile.d.ts.map +1 -0
- package/dist/runtime/auth-middleware.d.ts +34 -0
- package/dist/runtime/auth-middleware.d.ts.map +1 -0
- package/dist/runtime/boolean-columns.d.ts +65 -0
- package/dist/runtime/boolean-columns.d.ts.map +1 -0
- package/dist/runtime/cache.d.ts +62 -0
- package/dist/runtime/cache.d.ts.map +1 -0
- package/dist/runtime/create-route.d.ts +26 -0
- package/dist/runtime/create-route.d.ts.map +1 -0
- package/dist/runtime/delete-route.d.ts +15 -0
- package/dist/runtime/delete-route.d.ts.map +1 -0
- package/dist/runtime/drafts-route.d.ts +65 -0
- package/dist/runtime/drafts-route.d.ts.map +1 -0
- package/dist/runtime/health-route.d.ts +23 -0
- package/dist/runtime/health-route.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +24 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +17 -0
- package/dist/runtime/json-columns.d.ts +50 -0
- package/dist/runtime/json-columns.d.ts.map +1 -0
- package/dist/runtime/list-route.d.ts +20 -0
- package/dist/runtime/list-route.d.ts.map +1 -0
- package/dist/runtime/openapi-route.d.ts +30 -0
- package/dist/runtime/openapi-route.d.ts.map +1 -0
- package/dist/runtime/pattern-route.d.ts +48 -0
- package/dist/runtime/pattern-route.d.ts.map +1 -0
- package/dist/runtime/problem-details.d.ts +32 -0
- package/dist/runtime/problem-details.d.ts.map +1 -0
- package/dist/runtime/put-route.d.ts +19 -0
- package/dist/runtime/put-route.d.ts.map +1 -0
- package/dist/runtime/read-route.d.ts +26 -0
- package/dist/runtime/read-route.d.ts.map +1 -0
- package/dist/runtime/scale-cost.d.ts +84 -0
- package/dist/runtime/scale-cost.d.ts.map +1 -0
- package/dist/runtime/scheme-route.d.ts +49 -0
- package/dist/runtime/scheme-route.d.ts.map +1 -0
- package/dist/runtime/services.d.ts +42 -0
- package/dist/runtime/services.d.ts.map +1 -0
- package/dist/runtime/update-route.d.ts +20 -0
- package/dist/runtime/update-route.d.ts.map +1 -0
- package/dist/runtime/upload-route.d.ts +46 -0
- package/dist/runtime/upload-route.d.ts.map +1 -0
- package/dist/schema/index.d.ts +185 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +40 -0
- package/dist/schema/media.d.ts +237 -0
- package/dist/schema/media.d.ts.map +1 -0
- package/dist/schema/plugin-trust.d.ts +144 -0
- package/dist/schema/plugin-trust.d.ts.map +1 -0
- package/dist/signals/index.d.ts +10 -0
- package/dist/signals/index.d.ts.map +1 -0
- package/dist/signals/index.js +10 -0
- package/dist/storage/index.d.ts +120 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +13 -0
- package/dist/tenant/index.d.ts +105 -0
- package/dist/tenant/index.d.ts.map +1 -0
- package/dist/theme/index.d.ts +56 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/types/ids.d.ts +45 -0
- package/dist/types/ids.d.ts.map +1 -0
- package/dist/types/ids.js +15 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/user.d.ts +14 -0
- package/dist/types/user.d.ts.map +1 -0
- package/dist/types/user.js +1 -0
- package/package.json +116 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-grain access control per ADR 0006.
|
|
3
|
+
*
|
|
4
|
+
* 1. Collection-level (per-verb) — `read | create | update | delete`
|
|
5
|
+
* 2. Per-record predicates (post-fetch, when the predicate accepts a `record`)
|
|
6
|
+
* 3. Field-level (per-field) — declared via `Schema.X.annotations({ saacmsAccess })`
|
|
7
|
+
*
|
|
8
|
+
* Each predicate returns either a boolean (allow/deny) or a Where-clause to filter
|
|
9
|
+
* (read) / scope (update/delete). v1 alpha ships the types + a stub evaluator.
|
|
10
|
+
*/
|
|
11
|
+
import type { User } from "../types/user.ts";
|
|
12
|
+
export type AccessVerb = "read" | "create" | "update" | "delete";
|
|
13
|
+
/** Alias chosen for symmetry with Drizzle's `where` argument shape. */
|
|
14
|
+
export type WhereClause = Record<string, unknown>;
|
|
15
|
+
export type AccessResult = boolean | {
|
|
16
|
+
where: WhereClause;
|
|
17
|
+
};
|
|
18
|
+
export interface AccessContext<TRecord = unknown> {
|
|
19
|
+
readonly user: User | null;
|
|
20
|
+
readonly record?: TRecord;
|
|
21
|
+
}
|
|
22
|
+
export type AccessPredicate<TRecord = unknown> = (ctx: AccessContext<TRecord>) => AccessResult | Promise<AccessResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Field-level access semantics carried on Effect Schema annotations
|
|
25
|
+
* (`saacmsAccess: { read, update }`) per ADR 0006 §3.
|
|
26
|
+
*/
|
|
27
|
+
export interface FieldAccess<TRecord = unknown> {
|
|
28
|
+
readonly read?: AccessPredicate<TRecord>;
|
|
29
|
+
readonly update?: AccessPredicate<TRecord>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Evaluate an access predicate. Stub for v1 alpha — a real implementation will
|
|
33
|
+
* resolve the Effect (when predicate returns one), normalise the result, and
|
|
34
|
+
* apply Where-clause merging across multiple predicates.
|
|
35
|
+
*/
|
|
36
|
+
export declare function evaluateAccess<TRecord = unknown>(_predicate: AccessPredicate<TRecord>, _ctx: AccessContext<TRecord>): AccessResult;
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAE5C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAEhE,uEAAuE;AACvE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEjD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,CAAA;AAE3D,MAAM,WAAW,aAAa,CAAC,OAAO,GAAG,OAAO;IAC9C,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,MAAM,eAAe,CAAC,OAAO,GAAG,OAAO,IAAI,CAC/C,GAAG,EAAE,aAAa,CAAC,OAAO,CAAC,KACxB,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;AAEzC;;;GAGG;AACH,MAAM,WAAW,WAAW,CAAC,OAAO,GAAG,OAAO;IAC5C,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,OAAO,CAAC,CAAA;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAG,OAAO,EAC9C,UAAU,EAAE,eAAe,CAAC,OAAO,CAAC,EACpC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,GAC3B,YAAY,CAEd"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth adapter interface per ADR 0008 (Identity bounded context, ADR 0020).
|
|
3
|
+
*
|
|
4
|
+
* The `AuthAdapter` is the seam between saacms's runtime and whatever auth
|
|
5
|
+
* library the host wires (Better Auth in v1.0; Clerk / Auth.js / lucia in
|
|
6
|
+
* v1.x). saacms's runtime never imports better-auth directly — the host
|
|
7
|
+
* supplies an `AuthAdapter` implementation in `SaacmsConfig.auth`, and the
|
|
8
|
+
* `auth-middleware` calls into it before every authenticated route.
|
|
9
|
+
*
|
|
10
|
+
* The adapter contract is intentionally minimal: given an incoming request,
|
|
11
|
+
* return the `User` projection (per `types/user.ts`) or `null` for anonymous.
|
|
12
|
+
* Session creation, sign-in, sign-out, password reset, etc. all happen on the
|
|
13
|
+
* host's own routes (mounted by the host adapter), not on `/api/saacms/v1/*`.
|
|
14
|
+
*/
|
|
15
|
+
import type { User } from "../types/user.ts";
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a `User` (or `null` for anonymous) for an incoming request.
|
|
18
|
+
*
|
|
19
|
+
* Implementations typically inspect `Cookie:` and `Authorization:` headers,
|
|
20
|
+
* then call into the auth library's session-validation API.
|
|
21
|
+
*
|
|
22
|
+
* Errors should be thrown only for *transport* failures (e.g. session store
|
|
23
|
+
* unreachable) — invalid/expired/missing sessions resolve to `null` so the
|
|
24
|
+
* runtime can treat them uniformly as anonymous.
|
|
25
|
+
*/
|
|
26
|
+
export interface AuthAdapter {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
getSession(request: Request): Promise<User | null>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAE5C;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;CACnD"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block-Schema → content-migration projection per ADR 0012 (Migrations: schema
|
|
3
|
+
* and content) and ADR 0005 (Effect Schema is the single source of truth).
|
|
4
|
+
*
|
|
5
|
+
* Where `to-d1-migration.ts` projects a *Collection* Schema to the DDL that is
|
|
6
|
+
* the comparator for DB drift, this module projects a *Block* Schema to a
|
|
7
|
+
* structural **fingerprint** that is the comparator for *content* drift: when a
|
|
8
|
+
* Block's Schema evolves, every stored Page JSON that references that Block
|
|
9
|
+
* must be transformed so the next compile step can parse it.
|
|
10
|
+
*
|
|
11
|
+
* Four pure, deterministic exports:
|
|
12
|
+
*
|
|
13
|
+
* - `fingerprintBlockSchema(block)` — a stable structural fingerprint of a
|
|
14
|
+
* Block's Effect Schema (field name + projected type + optionality). The
|
|
15
|
+
* content analogue of `schemaToD1Migration`'s DDL string.
|
|
16
|
+
* - `diffBlockSchemas(prev, next)` — a structured added/removed/renamed/
|
|
17
|
+
* retyped description, sufficient to (a) decide a content migration is
|
|
18
|
+
* needed and (b) scaffold its transform body.
|
|
19
|
+
* - `contentMigrationModuleSource(slug, fromVersion, toVersion, diff)` — the
|
|
20
|
+
* **string** source of a runnable, type-correct
|
|
21
|
+
* `saacms/migrations/content/NNNN_<name>.ts` module: a typed `up(pageJson)`
|
|
22
|
+
* that walks a Page Block tree and transforms instances of the changed
|
|
23
|
+
* Block, plus a forward-only `down` stub.
|
|
24
|
+
* - `detectPendingContentMigrations({...})` — the pure refuse-to-start guard:
|
|
25
|
+
* for each Block whose current fingerprint ≠ its last-migrated fingerprint
|
|
26
|
+
* (with no corresponding applied content migration), a precise
|
|
27
|
+
* human-readable reason. Consumed by `saacms migrate check`; reusable
|
|
28
|
+
* verbatim by a future runtime-boot guard (kept pure for that reason).
|
|
29
|
+
*
|
|
30
|
+
* Block-version note (ADR 0012 + CONTEXT.md "Schema is the single source of
|
|
31
|
+
* truth; projections are mechanical"): the Block `version` lives on the dev's
|
|
32
|
+
* authored `defineBlock({...})` *structure*. saacms migrates *instances*
|
|
33
|
+
* (stored Page JSON); it must not rewrite the dev's authored structure source.
|
|
34
|
+
* So the generator records `from`/`to` versions in the emitted migration + the
|
|
35
|
+
* snapshot and the CLI logs an explicit "bump `version:`" instruction — it
|
|
36
|
+
* never edits a Block source file.
|
|
37
|
+
*/
|
|
38
|
+
import type { BlockDef } from "../schema/index.ts";
|
|
39
|
+
/** One projected field of a Block Schema. */
|
|
40
|
+
export interface BlockFieldShape {
|
|
41
|
+
readonly name: string;
|
|
42
|
+
/**
|
|
43
|
+
* Coarse projected type: `string` | `number` | `boolean` | `date` |
|
|
44
|
+
* `literal:<a|b|…>` (sorted) | `array` | `object` | `unknown`. Coarse on
|
|
45
|
+
* purpose — it is the same granularity a content transform can act on.
|
|
46
|
+
*/
|
|
47
|
+
readonly type: string;
|
|
48
|
+
readonly optional: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** A stable, order-independent structural fingerprint of a Block Schema. */
|
|
51
|
+
export interface BlockFingerprint {
|
|
52
|
+
readonly fields: ReadonlyArray<BlockFieldShape>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Compute the stable structural fingerprint of a Block's Effect Schema.
|
|
56
|
+
*
|
|
57
|
+
* Fields are sorted by name so the fingerprint is order-independent: the same
|
|
58
|
+
* Schema always produces the same fingerprint regardless of declaration order.
|
|
59
|
+
* Throws if the Block's root Schema is not a Struct (TypeLiteral) — the same
|
|
60
|
+
* invariant `schemaToD1Migration` enforces for Collections.
|
|
61
|
+
*/
|
|
62
|
+
export declare function fingerprintBlockSchema(block: BlockDef): BlockFingerprint;
|
|
63
|
+
/**
|
|
64
|
+
* Canonical stable string form of a fingerprint — the value stored in the
|
|
65
|
+
* migration snapshot and compared for drift (the content analogue of the DDL
|
|
66
|
+
* string the DB snapshot stores).
|
|
67
|
+
*/
|
|
68
|
+
export declare function fingerprintToString(fp: BlockFingerprint): string;
|
|
69
|
+
export interface AddedField {
|
|
70
|
+
readonly name: string;
|
|
71
|
+
readonly type: string;
|
|
72
|
+
readonly optional: boolean;
|
|
73
|
+
}
|
|
74
|
+
export interface RemovedField {
|
|
75
|
+
readonly name: string;
|
|
76
|
+
}
|
|
77
|
+
export interface RenamedField {
|
|
78
|
+
readonly from: string;
|
|
79
|
+
readonly to: string;
|
|
80
|
+
readonly type: string;
|
|
81
|
+
}
|
|
82
|
+
export interface RetypedField {
|
|
83
|
+
readonly name: string;
|
|
84
|
+
readonly fromType: string;
|
|
85
|
+
readonly toType: string;
|
|
86
|
+
}
|
|
87
|
+
export interface BlockSchemaDiff {
|
|
88
|
+
readonly added: ReadonlyArray<AddedField>;
|
|
89
|
+
readonly removed: ReadonlyArray<RemovedField>;
|
|
90
|
+
readonly renamed: ReadonlyArray<RenamedField>;
|
|
91
|
+
readonly retyped: ReadonlyArray<RetypedField>;
|
|
92
|
+
/** True iff any of the four buckets is non-empty (a content migration is warranted). */
|
|
93
|
+
readonly changed: boolean;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Diff two Block fingerprints into a structured change description.
|
|
97
|
+
*
|
|
98
|
+
* Rename heuristic: a removed field and an added field that share an identical
|
|
99
|
+
* projected type are paired as a rename (the common single-field case). This
|
|
100
|
+
* keeps the transform a safe key-copy rather than a drop+add data loss. Per
|
|
101
|
+
* ADR 0012 renames are otherwise an explicit dev/CLI conversation; the
|
|
102
|
+
* generated transform is reviewed before it is committed, so the heuristic is
|
|
103
|
+
* a scaffold, not an irreversible auto-decision.
|
|
104
|
+
*/
|
|
105
|
+
export declare function diffBlockSchemas(prev: BlockFingerprint, next: BlockFingerprint): BlockSchemaDiff;
|
|
106
|
+
/**
|
|
107
|
+
* Produce the **string** source of a runnable, type-correct content-migration
|
|
108
|
+
* module for `saacms/migrations/content/<NNNN>_<name>.ts`.
|
|
109
|
+
*
|
|
110
|
+
* The module exports:
|
|
111
|
+
* - `meta` — `{ block, fromVersion, toVersion }` (the version transition the
|
|
112
|
+
* runtime/`check` guard reads).
|
|
113
|
+
* - `up(pageJson: unknown): unknown` — deep-walks a Page Block tree and, on
|
|
114
|
+
* every node whose `type` equals the changed Block's slug, applies the
|
|
115
|
+
* diff (rename → copy old→new key; add → default; remove → drop; retype →
|
|
116
|
+
* a documented TODO with the diff inlined so the dev finishes intent).
|
|
117
|
+
* Non-matching nodes and all other Pages/Blocks are returned untouched.
|
|
118
|
+
* - `down(pageJson: unknown): never` — forward-only stub (rollback is
|
|
119
|
+
* git-revert + re-run per ADR 0012 "Deferred to v2").
|
|
120
|
+
*
|
|
121
|
+
* The generated source uses only language built-ins (no `@saacms/*` import) so
|
|
122
|
+
* it type-checks and runs standalone in the dev's repo.
|
|
123
|
+
*/
|
|
124
|
+
export declare function contentMigrationModuleSource(blockSlug: string, fromVersion: number, toVersion: number, diff: BlockSchemaDiff): string;
|
|
125
|
+
/** One Block with a pending content migration. */
|
|
126
|
+
export interface PendingContentMigration {
|
|
127
|
+
readonly slug: string;
|
|
128
|
+
readonly fromVersion: number;
|
|
129
|
+
readonly toVersion: number;
|
|
130
|
+
/** Precise, human-readable refuse-to-start reason. */
|
|
131
|
+
readonly reason: string;
|
|
132
|
+
}
|
|
133
|
+
export interface PendingContentReport {
|
|
134
|
+
readonly pending: ReadonlyArray<PendingContentMigration>;
|
|
135
|
+
readonly clean: boolean;
|
|
136
|
+
}
|
|
137
|
+
/** The per-Block record stored under the snapshot's `$blocks` section. */
|
|
138
|
+
export interface BlockSnapshotEntry {
|
|
139
|
+
/** `fingerprintToString` of the last *migrated* Block Schema. */
|
|
140
|
+
readonly fingerprint: string;
|
|
141
|
+
/** Block version the last migration migrated *to*. */
|
|
142
|
+
readonly version: number;
|
|
143
|
+
/** How many content migrations have been generated for this Block. */
|
|
144
|
+
readonly migrationCount: number;
|
|
145
|
+
}
|
|
146
|
+
export interface DetectPendingInput {
|
|
147
|
+
readonly blocks: ReadonlyArray<BlockDef>;
|
|
148
|
+
/** The `$blocks` section of the migration snapshot (last-migrated state). */
|
|
149
|
+
readonly snapshot: Readonly<Record<string, BlockSnapshotEntry>>;
|
|
150
|
+
/**
|
|
151
|
+
* Filenames of content migrations already applied (e.g. from a content
|
|
152
|
+
* ledger). Optional: when supplied, a Block whose fingerprint matches the
|
|
153
|
+
* snapshot but whose recorded migration filename is absent here is still
|
|
154
|
+
* reported (generated-but-not-applied). When omitted, pendingness is derived
|
|
155
|
+
* purely from fingerprint-vs-last-migrated drift.
|
|
156
|
+
*/
|
|
157
|
+
readonly appliedMigrations?: ReadonlyArray<string>;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Pure refuse-to-start guard. For each Block whose current Schema fingerprint
|
|
161
|
+
* differs from its last-migrated fingerprint (or whose generated migration is
|
|
162
|
+
* not in `appliedMigrations`), return a precise reason. A Block with no
|
|
163
|
+
* snapshot entry is NOT pending — it has no stored instances on a prior
|
|
164
|
+
* version to transform (the content analogue of a brand-new table).
|
|
165
|
+
*/
|
|
166
|
+
export declare function detectPendingContentMigrations(input: DetectPendingInput): PendingContentReport;
|
|
167
|
+
//# sourceMappingURL=content-migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-migration.d.ts","sourceRoot":"","sources":["../../src/codegen/content-migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-user OpenAPI filtering per ADR 0006 + ADR 0018.
|
|
3
|
+
*
|
|
4
|
+
* The runtime calls `filterOpenApiForUser` on every `GET /api/saacms/v1/openapi.json`
|
|
5
|
+
* request, after assembling the full spec from `collectionToOpenApiPaths`. The
|
|
6
|
+
* result is the slice of routes + fields the *calling* user is allowed to see.
|
|
7
|
+
*
|
|
8
|
+
* --------------------------------------------------------------------
|
|
9
|
+
* Mapping HTTP method → AccessVerb (per ADR 0006 + ADR 0018)
|
|
10
|
+
* --------------------------------------------------------------------
|
|
11
|
+
*
|
|
12
|
+
* GET → read
|
|
13
|
+
* POST (create) → create
|
|
14
|
+
* PUT, PATCH → update
|
|
15
|
+
* DELETE → delete
|
|
16
|
+
* POST /{...}:verb → update (custom-action default for v1;
|
|
17
|
+
* TODO: per-action override)
|
|
18
|
+
*
|
|
19
|
+
* Note: per the no-SSE-dependency principle, the v1 spec does not advertise a
|
|
20
|
+
* `/subscribe` path. If a host installs an opt-in realtime plugin that adds
|
|
21
|
+
* one, that plugin is responsible for declaring its own filter mapping.
|
|
22
|
+
*
|
|
23
|
+
* --------------------------------------------------------------------
|
|
24
|
+
* Predicate semantics (callable out for review)
|
|
25
|
+
* --------------------------------------------------------------------
|
|
26
|
+
*
|
|
27
|
+
* 1. The predicate is invoked with `{ user, record: undefined }`. A `false`
|
|
28
|
+
* return removes the operation; anything else (`true`, `{ where }`, or a
|
|
29
|
+
* Promise resolving to either) keeps it. Where-clauses scope returned
|
|
30
|
+
* records at fetch time — they don't hide the route at the spec level.
|
|
31
|
+
*
|
|
32
|
+
* 2. Per-record predicates (those that destructure or de-reference `record`)
|
|
33
|
+
* can't be meaningfully evaluated without a record. If such a predicate
|
|
34
|
+
* throws on `record: undefined`, we treat it as "would-allow at the listing
|
|
35
|
+
* level" per ADR 0006: hiding the route would over-deny; the per-record
|
|
36
|
+
* filter still fires at fetch time.
|
|
37
|
+
*
|
|
38
|
+
* 3. `null` user is passed through to the predicate; the predicate decides what
|
|
39
|
+
* anonymous gets. Collections with NO `access` declared default every verb
|
|
40
|
+
* to allowed (open by default — devs opt in to access control).
|
|
41
|
+
*
|
|
42
|
+
* --------------------------------------------------------------------
|
|
43
|
+
* Field-level filtering
|
|
44
|
+
* --------------------------------------------------------------------
|
|
45
|
+
*
|
|
46
|
+
* For each Collection we walk its Effect Schema AST; on every `PropertySignature`
|
|
47
|
+
* we read the `saacmsAccess` annotation (per ADR 0006 §3 + ADR 0005 annotation
|
|
48
|
+
* namespace). If `saacmsAccess.read` returns false for the user, the property
|
|
49
|
+
* is stripped from `components.schemas.{PascalSlug}.properties` and from the
|
|
50
|
+
* `required` array. Per-record field evaluation stays a runtime concern.
|
|
51
|
+
*
|
|
52
|
+
* --------------------------------------------------------------------
|
|
53
|
+
* Immutability + caching
|
|
54
|
+
* --------------------------------------------------------------------
|
|
55
|
+
*
|
|
56
|
+
* The runtime cache (per ADR 0021) shares the full-spec snapshot across requests
|
|
57
|
+
* and re-filters per "access fingerprint" (role + relevant claims). To keep that
|
|
58
|
+
* sharing safe we deep-clone the input via `structuredClone` and never mutate
|
|
59
|
+
* what was passed in. The output is a fresh tree.
|
|
60
|
+
*
|
|
61
|
+
* --------------------------------------------------------------------
|
|
62
|
+
* Reference pruning
|
|
63
|
+
* --------------------------------------------------------------------
|
|
64
|
+
*
|
|
65
|
+
* After path filtering we walk every surviving Path Item collecting
|
|
66
|
+
* `$ref: "#/components/schemas/X"` strings, then transitively walk the kept
|
|
67
|
+
* schemas. Schemas not in that set are dropped from `components.schemas`.
|
|
68
|
+
* `ProblemDetails` is always preserved — every error response references it.
|
|
69
|
+
*/
|
|
70
|
+
import type { CollectionDef } from "../schema/index.ts";
|
|
71
|
+
import type { User } from "../types/user.ts";
|
|
72
|
+
import type { OpenAPIPathsObject, OpenAPISchemaObject } from "./openapi-types.ts";
|
|
73
|
+
/**
|
|
74
|
+
* The slice of an OpenAPI document this filter operates on. `paths` and
|
|
75
|
+
* `components.schemas` are the only parts whose contents are user-dependent;
|
|
76
|
+
* `info`, `servers`, `tags`, etc. are caller-attached and pass through unchanged
|
|
77
|
+
* outside this filter.
|
|
78
|
+
*/
|
|
79
|
+
export interface FilterableSpec {
|
|
80
|
+
readonly paths: OpenAPIPathsObject;
|
|
81
|
+
readonly components: {
|
|
82
|
+
readonly schemas: Record<string, OpenAPISchemaObject>;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export interface FilterOpenApiForUserInput {
|
|
86
|
+
readonly spec: FilterableSpec;
|
|
87
|
+
readonly collections: ReadonlyArray<CollectionDef>;
|
|
88
|
+
/** `null` is anonymous; the predicate decides what anonymous gets. */
|
|
89
|
+
readonly user: User | null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Return a NEW spec scoped to what `user` is allowed to see:
|
|
93
|
+
* operations whose verb-mapped predicate returns false are removed; paths whose
|
|
94
|
+
* operations are all removed are pruned; fields with `saacmsAccess.read`
|
|
95
|
+
* returning false are stripped from the Collection's Schema Object; finally
|
|
96
|
+
* unreferenced `components.schemas` entries are pruned (best-effort $ref walk,
|
|
97
|
+
* with `ProblemDetails` always preserved).
|
|
98
|
+
*/
|
|
99
|
+
export declare function filterOpenApiForUser(input: FilterOpenApiForUserInput): Promise<FilterableSpec>;
|
|
100
|
+
//# sourceMappingURL=filter-openapi-for-user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-openapi-for-user.d.ts","sourceRoot":"","sources":["../../src/codegen/filter-openapi-for-user.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AAQH,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,KAAK,EAEV,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,oBAAoB,CAAA;AAM3B;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAA;IAClC,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;KACtD,CAAA;CACF;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IAClD,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,cAAc,CAAC,CAoDzB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @saacms/core — codegen surface.
|
|
3
|
+
*
|
|
4
|
+
* Schema → projection generators per ADR 0005 (Effect Schema is the single
|
|
5
|
+
* source of truth) — Drizzle table, OpenAPI spec, TS types, Puck field config.
|
|
6
|
+
*
|
|
7
|
+
* Each projection is a pure function of the Schema (and CollectionDef where
|
|
8
|
+
* relevant); they're called from `saacms codegen` (CLI) and from the runtime
|
|
9
|
+
* when assembling per-user OpenAPI documents.
|
|
10
|
+
*/
|
|
11
|
+
export * from "./openapi-types.ts";
|
|
12
|
+
export * from "./to-openapi.ts";
|
|
13
|
+
export * from "./to-drizzle.ts";
|
|
14
|
+
export * from "./to-d1-migration.ts";
|
|
15
|
+
export * from "./content-migration.ts";
|
|
16
|
+
export * from "./to-puck-fields.ts";
|
|
17
|
+
export * from "./to-ts-types.ts";
|
|
18
|
+
export * from "./filter-openapi-for-user.ts";
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/codegen/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA;AAChC,cAAc,8BAA8B,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertNoTableNameCollisions,
|
|
3
|
+
camelToSnakeCase,
|
|
4
|
+
collectionToOpenApiPaths,
|
|
5
|
+
contentMigrationModuleSource,
|
|
6
|
+
defaultLabel,
|
|
7
|
+
detectPendingContentMigrations,
|
|
8
|
+
diffBlockSchemas,
|
|
9
|
+
filterOpenApiForUser,
|
|
10
|
+
fingerprintBlockSchema,
|
|
11
|
+
fingerprintToString,
|
|
12
|
+
normalizeDateSchemas,
|
|
13
|
+
schemaToD1Migration,
|
|
14
|
+
schemaToD1Migrations,
|
|
15
|
+
schemaToDrizzle,
|
|
16
|
+
schemaToOpenApiSchema,
|
|
17
|
+
schemaToPuckFields,
|
|
18
|
+
schemaToTsType,
|
|
19
|
+
schemaToTsTypes,
|
|
20
|
+
slugToTableName
|
|
21
|
+
} from "../index-a3pnt8yz.js";
|
|
22
|
+
import"../index-8g8ymd37.js";
|
|
23
|
+
export {
|
|
24
|
+
slugToTableName,
|
|
25
|
+
schemaToTsTypes,
|
|
26
|
+
schemaToTsType,
|
|
27
|
+
schemaToPuckFields,
|
|
28
|
+
schemaToOpenApiSchema,
|
|
29
|
+
schemaToDrizzle,
|
|
30
|
+
schemaToD1Migrations,
|
|
31
|
+
schemaToD1Migration,
|
|
32
|
+
normalizeDateSchemas,
|
|
33
|
+
fingerprintToString,
|
|
34
|
+
fingerprintBlockSchema,
|
|
35
|
+
filterOpenApiForUser,
|
|
36
|
+
diffBlockSchemas,
|
|
37
|
+
detectPendingContentMigrations,
|
|
38
|
+
defaultLabel,
|
|
39
|
+
contentMigrationModuleSource,
|
|
40
|
+
collectionToOpenApiPaths,
|
|
41
|
+
camelToSnakeCase,
|
|
42
|
+
assertNoTableNameCollisions
|
|
43
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal local OpenAPI 3.1 type subset for emission.
|
|
3
|
+
*
|
|
4
|
+
* We deliberately avoid pulling `openapi-types` as a runtime dep (it brings the
|
|
5
|
+
* full 3.0/3.1 surface plus historical baggage). Per ADR 0018 saacms only emits
|
|
6
|
+
* a constrained subset; this file covers exactly that subset.
|
|
7
|
+
*
|
|
8
|
+
* Shapes follow the OpenAPI Specification 3.1.0
|
|
9
|
+
* (https://spec.openapis.org/oas/v3.1.0). For the schema-object shape we lean
|
|
10
|
+
* on Effect's `JSONSchema.make(..., { target: "openApi3.1" })` projection,
|
|
11
|
+
* which already aligns with JSON Schema 2020-12 (the dialect OpenAPI 3.1 uses).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* OpenAPI 3.1 Schema Object. Open-shape; we only spell out the fields we
|
|
15
|
+
* actively emit so authoring stays type-safe but downstream merging stays
|
|
16
|
+
* permissive. See OAS 3.1 §4.7.24.
|
|
17
|
+
*/
|
|
18
|
+
export interface OpenAPISchemaObject {
|
|
19
|
+
readonly $ref?: string;
|
|
20
|
+
readonly type?: "string" | "number" | "integer" | "boolean" | "object" | "array" | "null" | ReadonlyArray<"string" | "number" | "integer" | "boolean" | "object" | "array" | "null">;
|
|
21
|
+
readonly format?: string;
|
|
22
|
+
readonly title?: string;
|
|
23
|
+
readonly description?: string;
|
|
24
|
+
readonly default?: unknown;
|
|
25
|
+
readonly example?: unknown;
|
|
26
|
+
readonly examples?: ReadonlyArray<unknown>;
|
|
27
|
+
readonly enum?: ReadonlyArray<unknown>;
|
|
28
|
+
readonly const?: unknown;
|
|
29
|
+
readonly nullable?: boolean;
|
|
30
|
+
readonly deprecated?: boolean;
|
|
31
|
+
readonly readOnly?: boolean;
|
|
32
|
+
readonly writeOnly?: boolean;
|
|
33
|
+
readonly minLength?: number;
|
|
34
|
+
readonly maxLength?: number;
|
|
35
|
+
readonly pattern?: string;
|
|
36
|
+
readonly contentMediaType?: string;
|
|
37
|
+
readonly minimum?: number;
|
|
38
|
+
readonly maximum?: number;
|
|
39
|
+
readonly exclusiveMinimum?: number | boolean;
|
|
40
|
+
readonly exclusiveMaximum?: number | boolean;
|
|
41
|
+
readonly multipleOf?: number;
|
|
42
|
+
readonly items?: OpenAPISchemaObject | ReadonlyArray<OpenAPISchemaObject>;
|
|
43
|
+
readonly minItems?: number;
|
|
44
|
+
readonly maxItems?: number;
|
|
45
|
+
readonly uniqueItems?: boolean;
|
|
46
|
+
readonly prefixItems?: ReadonlyArray<OpenAPISchemaObject>;
|
|
47
|
+
readonly properties?: Readonly<Record<string, OpenAPISchemaObject>>;
|
|
48
|
+
readonly required?: ReadonlyArray<string>;
|
|
49
|
+
readonly additionalProperties?: boolean | OpenAPISchemaObject;
|
|
50
|
+
readonly patternProperties?: Readonly<Record<string, OpenAPISchemaObject>>;
|
|
51
|
+
readonly allOf?: ReadonlyArray<OpenAPISchemaObject>;
|
|
52
|
+
readonly anyOf?: ReadonlyArray<OpenAPISchemaObject>;
|
|
53
|
+
readonly oneOf?: ReadonlyArray<OpenAPISchemaObject>;
|
|
54
|
+
readonly not?: OpenAPISchemaObject;
|
|
55
|
+
readonly [key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
export type OpenAPIParameterIn = "query" | "header" | "path" | "cookie";
|
|
58
|
+
export interface OpenAPIParameter {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
readonly in: OpenAPIParameterIn;
|
|
61
|
+
readonly description?: string;
|
|
62
|
+
readonly required?: boolean;
|
|
63
|
+
readonly deprecated?: boolean;
|
|
64
|
+
readonly allowEmptyValue?: boolean;
|
|
65
|
+
readonly schema?: OpenAPISchemaObject;
|
|
66
|
+
readonly example?: unknown;
|
|
67
|
+
readonly examples?: Readonly<Record<string, OpenAPIExample>>;
|
|
68
|
+
readonly content?: Readonly<Record<string, OpenAPIMediaType>>;
|
|
69
|
+
}
|
|
70
|
+
export interface OpenAPIExample {
|
|
71
|
+
readonly summary?: string;
|
|
72
|
+
readonly description?: string;
|
|
73
|
+
readonly value?: unknown;
|
|
74
|
+
readonly externalValue?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface OpenAPIHeader {
|
|
77
|
+
readonly description?: string;
|
|
78
|
+
readonly required?: boolean;
|
|
79
|
+
readonly deprecated?: boolean;
|
|
80
|
+
readonly schema?: OpenAPISchemaObject;
|
|
81
|
+
readonly example?: unknown;
|
|
82
|
+
}
|
|
83
|
+
export interface OpenAPIMediaType {
|
|
84
|
+
readonly schema?: OpenAPISchemaObject;
|
|
85
|
+
readonly example?: unknown;
|
|
86
|
+
readonly examples?: Readonly<Record<string, OpenAPIExample>>;
|
|
87
|
+
}
|
|
88
|
+
export interface OpenAPIRequestBody {
|
|
89
|
+
readonly description?: string;
|
|
90
|
+
readonly required?: boolean;
|
|
91
|
+
readonly content: Readonly<Record<string, OpenAPIMediaType>>;
|
|
92
|
+
}
|
|
93
|
+
export interface OpenAPIResponse {
|
|
94
|
+
readonly description: string;
|
|
95
|
+
readonly headers?: Readonly<Record<string, OpenAPIHeader>>;
|
|
96
|
+
readonly content?: Readonly<Record<string, OpenAPIMediaType>>;
|
|
97
|
+
}
|
|
98
|
+
export type OpenAPIResponses = Readonly<Record<string, OpenAPIResponse>>;
|
|
99
|
+
export type OpenAPISecurityRequirement = Readonly<Record<string, ReadonlyArray<string>>>;
|
|
100
|
+
export interface OpenAPIOperation {
|
|
101
|
+
readonly tags?: ReadonlyArray<string>;
|
|
102
|
+
readonly summary?: string;
|
|
103
|
+
readonly description?: string;
|
|
104
|
+
readonly operationId?: string;
|
|
105
|
+
readonly parameters?: ReadonlyArray<OpenAPIParameter>;
|
|
106
|
+
readonly requestBody?: OpenAPIRequestBody;
|
|
107
|
+
readonly responses: OpenAPIResponses;
|
|
108
|
+
readonly security?: ReadonlyArray<OpenAPISecurityRequirement>;
|
|
109
|
+
readonly deprecated?: boolean;
|
|
110
|
+
}
|
|
111
|
+
export interface OpenAPIPathItem {
|
|
112
|
+
readonly summary?: string;
|
|
113
|
+
readonly description?: string;
|
|
114
|
+
readonly get?: OpenAPIOperation;
|
|
115
|
+
readonly put?: OpenAPIOperation;
|
|
116
|
+
readonly post?: OpenAPIOperation;
|
|
117
|
+
readonly delete?: OpenAPIOperation;
|
|
118
|
+
readonly options?: OpenAPIOperation;
|
|
119
|
+
readonly head?: OpenAPIOperation;
|
|
120
|
+
readonly patch?: OpenAPIOperation;
|
|
121
|
+
readonly trace?: OpenAPIOperation;
|
|
122
|
+
readonly parameters?: ReadonlyArray<OpenAPIParameter>;
|
|
123
|
+
}
|
|
124
|
+
export type OpenAPIPathsObject = Readonly<Record<string, OpenAPIPathItem>>;
|
|
125
|
+
//# sourceMappingURL=openapi-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-types.d.ts","sourceRoot":"","sources":["../../src/codegen/openapi-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema → D1 migration SQL projection per ADR 0005 (Effect Schema is the
|
|
3
|
+
* single source of truth) and ADR 0024 (v1 alpha targets D1 / SQLite).
|
|
4
|
+
*
|
|
5
|
+
* Two functions:
|
|
6
|
+
* - `schemaToD1Migration(coll)` — projects ONE Collection to a
|
|
7
|
+
* `CREATE TABLE IF NOT EXISTS ...` statement.
|
|
8
|
+
* - `schemaToD1Migrations(colls)` — joins per-collection statements with a
|
|
9
|
+
* blank line, in the order given. Caller orders tables so that any FK
|
|
10
|
+
* target precedes its referrer (v1 does not topo-sort).
|
|
11
|
+
*
|
|
12
|
+
* This is the SQL form of `to-drizzle.ts`'s in-memory Drizzle table. The two
|
|
13
|
+
* agree on column names (snake_case for the SQL form), types, NOT NULL,
|
|
14
|
+
* defaults, and FK constraints.
|
|
15
|
+
*
|
|
16
|
+
* Annotation namespace recognised (mirrors `to-drizzle.ts`):
|
|
17
|
+
* - `saacmsRef: <CollectionSlug>` — foreign key to `<slug>.id`.
|
|
18
|
+
* - `saacmsInt: true` — narrows `Schema.Number` from REAL to INTEGER.
|
|
19
|
+
* - `saacmsLength: number` — max length hint (no SQLite enforcement).
|
|
20
|
+
* - `saacmsRefOnDelete: "cascade"|"restrict"|"set null"|"no action"` —
|
|
21
|
+
* overrides the default `cascade` ON DELETE action.
|
|
22
|
+
*
|
|
23
|
+
* Auto-injected columns (always present, override user-defined collisions):
|
|
24
|
+
* - `id` TEXT PRIMARY KEY NOT NULL
|
|
25
|
+
* - `created_at` TEXT NOT NULL DEFAULT (datetime('now'))
|
|
26
|
+
* - `updated_at` TEXT NOT NULL DEFAULT (datetime('now'))
|
|
27
|
+
*
|
|
28
|
+
* Type mapping (Effect → SQLite):
|
|
29
|
+
* - `Schema.String` → TEXT
|
|
30
|
+
* - `Schema.Number` → REAL (INTEGER with `saacmsInt`)
|
|
31
|
+
* - `Schema.Boolean` → INTEGER (stored 0/1)
|
|
32
|
+
* - `Schema.Date` / DateFromString → TEXT (ISO-8601)
|
|
33
|
+
* - `Schema.Literal("a","b",…)` → TEXT + `CHECK (col IN ('a','b',…))`
|
|
34
|
+
* - `Schema.Array(X)` / nested Struct → TEXT (JSON at the runtime layer —
|
|
35
|
+
* `encodeJsonColumns`/`decodeJsonColumns` in `../runtime/json-columns.ts`
|
|
36
|
+
* own the (de)serialisation at the runtime storage boundary; the adapter
|
|
37
|
+
* stays schema-agnostic per ADR 0020).
|
|
38
|
+
*
|
|
39
|
+
* Physical table name: the external Collection slug is the data contract (route
|
|
40
|
+
* path + cache tag) and MAY contain characters illegal in a SQL identifier
|
|
41
|
+
* (e.g. the Payload-idiomatic `discount-codes`). `slugToTableName` projects the
|
|
42
|
+
* slug to a deterministic `IDENT_RE`-valid physical table identifier; only the
|
|
43
|
+
* DDL table name + FK `REFERENCES` targets use the projected form. The runtime
|
|
44
|
+
* passes the same projection to every `rows.*` call so DDL and queries agree.
|
|
45
|
+
*/
|
|
46
|
+
import type { CollectionDef } from "../schema/index.ts";
|
|
47
|
+
/**
|
|
48
|
+
* Project a camelCase domain key to its snake_case physical column name.
|
|
49
|
+
*
|
|
50
|
+
* This is the canonical naming convention (ADR 0005 — Schema is the single
|
|
51
|
+
* source of truth, the DB is a mechanical projection). It is exported so the
|
|
52
|
+
* Storage anti-corruption layer (`@saacms/storage-d1`, ADR 0020) reuses the
|
|
53
|
+
* *same* function rather than re-deriving the convention — single source of
|
|
54
|
+
* truth for the snake↔camel boundary.
|
|
55
|
+
*
|
|
56
|
+
* `id` and other single-word keys map to themselves.
|
|
57
|
+
*/
|
|
58
|
+
export declare function camelToSnakeCase(name: string): string;
|
|
59
|
+
/** Project an external Collection slug to a deterministic `IDENT_RE`-valid physical table name. */
|
|
60
|
+
export declare function slugToTableName(slug: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Throw precisely when two DISTINCT external slugs in a single config project
|
|
63
|
+
* to the same physical table name (e.g. `discount-codes` + `discount_codes`).
|
|
64
|
+
* Without this guard the collision would silently merge two Collections' rows
|
|
65
|
+
* into one table — a loud config-time error is the correct disposition.
|
|
66
|
+
*/
|
|
67
|
+
export declare function assertNoTableNameCollisions(colls: ReadonlyArray<{
|
|
68
|
+
readonly slug: CollectionSlugLike;
|
|
69
|
+
}>): void;
|
|
70
|
+
type CollectionSlugLike = CollectionDef["slug"];
|
|
71
|
+
/**
|
|
72
|
+
* Project a Collection to a `CREATE TABLE IF NOT EXISTS ...` statement.
|
|
73
|
+
*
|
|
74
|
+
* The output is the SQL `wrangler d1 migrations create` consumes. Whitespace
|
|
75
|
+
* is intentionally pretty (one column per line) so generated migration files
|
|
76
|
+
* are diff-friendly.
|
|
77
|
+
*/
|
|
78
|
+
export declare function schemaToD1Migration(coll: CollectionDef): string;
|
|
79
|
+
/**
|
|
80
|
+
* Project an ordered list of Collections to a single SQL script with one
|
|
81
|
+
* blank line between statements.
|
|
82
|
+
*
|
|
83
|
+
* The caller is responsible for ordering: tables referenced by FK constraints
|
|
84
|
+
* should appear before their referrers. v1 does not topo-sort.
|
|
85
|
+
*/
|
|
86
|
+
export declare function schemaToD1Migrations(colls: ReadonlyArray<CollectionDef>): string;
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=to-d1-migration.d.ts.map
|