@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 @@
1
+ {"version":3,"file":"to-d1-migration.d.ts","sourceRoot":"","sources":["../../src/codegen/to-d1-migration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAqFvD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAsBD,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASpD;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,aAAa,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAA;CAAE,CAAC,GAC1D,IAAI,CAgBN;AAED,KAAK,kBAAkB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;AA+E/C;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAqD/D;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAClC,MAAM,CAMR"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Schema → Drizzle table projection per ADR 0005 (Effect Schema is the single
3
+ * source of truth) and ADR 0024 (v1 alpha targets D1/SQLite, so we emit
4
+ * `drizzle-orm/sqlite-core` tables).
5
+ *
6
+ * One function:
7
+ * - `schemaToDrizzle(slug, schema)` — projects an Effect Schema struct to a
8
+ * Drizzle SQLite table with auto-injected `id`, `createdAt`, `updatedAt`.
9
+ *
10
+ * --------------------------------------------------------------------
11
+ * Annotation namespace (saacms-prefixed; mirrors `to-openapi.ts` convention)
12
+ * --------------------------------------------------------------------
13
+ *
14
+ * The following Effect-Schema annotations are recognised on per-property AST
15
+ * nodes (read with `.annotations({ ... })`). All keys are saacms-prefixed
16
+ * strings (per ADR 0005 §Consequences — "Annotation collision avoidance").
17
+ *
18
+ * `saacmsRef: <CollectionSlug>` — marks a `Schema.String` field as a foreign
19
+ * key to another Collection's `id` column. v1 emits a Drizzle `references`
20
+ * callback that resolves through an internal placeholder registry (the
21
+ * foreign Collection's table doesn't have to be in scope at projection
22
+ * time — the codegen-CLI assembler swaps the placeholder for the real
23
+ * table later). Cascading delete defaults to `cascade` per task spec
24
+ * (most natural for saacms); override later via a config knob if needed.
25
+ *
26
+ * `saacmsInt: true` — narrows a `Schema.Number` field from `real` (SQLite
27
+ * REAL / IEEE-754 double) to `integer`. Use when the field is logically
28
+ * an integer count (e.g. `views`, `priority`).
29
+ *
30
+ * `saacmsLength: number` — max length on a `Schema.String`. SQLite stores
31
+ * TEXT regardless, but Drizzle's text builder accepts a `length` config
32
+ * for dialect dialects that honour it; harmless on SQLite, useful when
33
+ * the same Schema is reprojected for postgres/mysql in v1.x.
34
+ *
35
+ * `saacmsRefOnDelete: "cascade" | "restrict" | "set null" | "no action"` —
36
+ * overrides the default `cascade` behaviour for a `saacmsRef` field.
37
+ *
38
+ * --------------------------------------------------------------------
39
+ * AST-walking decisions (callable out for review)
40
+ * --------------------------------------------------------------------
41
+ *
42
+ * 1. The root schema MUST be a `TypeLiteral` (i.e. `Schema.Struct({...})`).
43
+ * Suspended / refined / union root schemas throw — we want a single canonical
44
+ * table shape per Collection.
45
+ *
46
+ * 2. A field is treated as nullable (omit `.notNull()`) when EITHER
47
+ * a) the PropertySignature has `isOptional: true` (i.e. `key?:` in the
48
+ * encoded type), OR
49
+ * b) the property's type is a `Union` containing `UndefinedKeyword` or
50
+ * `NullKeyword`.
51
+ * Both are produced by `Schema.optional(X)` / `Schema.NullOr(X)`. We strip
52
+ * those wrappers to get the inner "data" type before mapping to a column.
53
+ *
54
+ * 3. `Refinement` wrappers (e.g. `Schema.String.pipe(Schema.minLength(1))`)
55
+ * are walked through — we look at `refinement.from` recursively until we
56
+ * hit a non-refinement. SQLite has no `CHECK length(x) >= 1` enforced by
57
+ * Drizzle's column builder; the validation lives in the Effect Schema and
58
+ * runs at the application boundary (per ADR 0005).
59
+ *
60
+ * 4. `Schema.Array(X)` becomes `text(name, { mode: "json" })` for v1. Dedicated
61
+ * junction tables (the structurally-correct projection) are deferred — JSON
62
+ * column is the pragmatic SQLite default and matches what every CMS that
63
+ * targets D1 ships today.
64
+ *
65
+ * 5. `Schema.Date` / `Schema.DateFromSelf` are detected by Effect's identifier
66
+ * annotation (`getIdentifierAnnotation(ast) === "DateFromSelf"`). Both
67
+ * project to `integer(name, { mode: "timestamp" })` — SQLite stores Unix
68
+ * seconds, Drizzle hydrates to JS `Date`.
69
+ *
70
+ * 6. Annotations are read off the inner data type's annotations (after
71
+ * union-stripping). `Schema.optional(Schema.String.annotations({saacmsRef: "x"}))`
72
+ * works; the annotation is on the String, not on the optional wrapper.
73
+ *
74
+ * 7. The synthetic `id`, `createdAt`, `updatedAt` columns are ALWAYS injected
75
+ * and override any user-defined columns of the same name. Logged in dev as
76
+ * a TODO when collision is detected (left for the codegen-CLI assembler).
77
+ *
78
+ * --------------------------------------------------------------------
79
+ * Foreign-key resolution (placeholder registry)
80
+ * --------------------------------------------------------------------
81
+ *
82
+ * Drizzle's `references(callback, actions)` takes a thunk returning a real
83
+ * `SQLiteColumn`. At projection time the foreign Collection's table object
84
+ * may not exist yet (chicken-and-egg when both Collections reference each
85
+ * other). We resolve this with a per-projection placeholder map:
86
+ *
87
+ * - First time we see `saacmsRef: "users"`, we synthesize a stub
88
+ * `usersPlaceholder = sqliteTable("users", { id: text("id").primaryKey() })`
89
+ * and cache it.
90
+ * - The reference thunk returns `usersPlaceholder.id`.
91
+ * - The codegen-CLI assembler (when it eventually merges all real tables)
92
+ * replaces placeholders with actual table refs. For v1 unit-test
93
+ * purposes, `getTableConfig(...).foreignKeys[i].reference()` returns a
94
+ * well-formed `{ foreignTable, foreignColumns }` shape with the right
95
+ * table NAME, which is what tests assert against.
96
+ */
97
+ import type { Schema } from "effect";
98
+ /**
99
+ * Project an Effect Schema struct to a Drizzle SQLite table.
100
+ *
101
+ * Auto-injects `id` (text PK), `createdAt`, `updatedAt` (integer timestamps
102
+ * with default `() => new Date()`).
103
+ *
104
+ * @param slug — the Collection slug (also used as the table name)
105
+ * @param schema — an `effect/Schema` whose root is a `Schema.Struct({...})`
106
+ */
107
+ export declare function schemaToDrizzle<TSchema extends Schema.Schema<any, any, any>>(slug: string, schema: TSchema): import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
108
+ name: string;
109
+ schema: undefined;
110
+ columns: {
111
+ [x: string]: import("drizzle-orm/sqlite-core").SQLiteColumn<{
112
+ name: string;
113
+ tableName: string;
114
+ dataType: import("drizzle-orm").ColumnDataType;
115
+ columnType: string;
116
+ data: unknown;
117
+ driverParam: unknown;
118
+ notNull: false;
119
+ hasDefault: false;
120
+ isPrimaryKey: false;
121
+ isAutoincrement: false;
122
+ hasRuntimeDefault: false;
123
+ enumValues: string[] | undefined;
124
+ baseColumn: never;
125
+ identity: undefined;
126
+ generated: undefined;
127
+ }, object>;
128
+ };
129
+ dialect: "sqlite";
130
+ }>;
131
+ //# sourceMappingURL=to-drizzle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-drizzle.d.ts","sourceRoot":"","sources":["../../src/codegen/to-drizzle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+FG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAsLpC;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1E,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;GAuChB"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Schema → OpenAPI projections per ADR 0005 (Schema is the source) and
3
+ * ADR 0018 (the API design contract every endpoint follows).
4
+ *
5
+ * Two functions:
6
+ * - `schemaToOpenApiSchema(schema)` — projects an Effect Schema to an
7
+ * OpenAPI 3.1 Schema Object (also a JSON Schema 2020-12 document, since
8
+ * OAS 3.1 aligns with that JSON Schema dialect).
9
+ * - `collectionToOpenApiPaths(coll, opts?)` — emits the canonical CRUD path
10
+ * set for a Collection, ETag/Idempotency-Key/If-Match conventions,
11
+ * Problem Details error responses, and tags.
12
+ *
13
+ * Implementation notes (edge-case decisions made here, callable out for review):
14
+ *
15
+ * 1. We pass `target: "openApi3.1"` to Effect's `JSONSchema.make`. That dialect
16
+ * aligns with JSON Schema 2020-12 (OAS 3.1's chosen dialect — see OAS 3.1
17
+ * §4.7.24.5), so the output is already OpenAPI-shaped: it uses
18
+ * `type: ["string","null"]` arrays for nullable rather than the OAS 3.0
19
+ * `nullable: true` keyword. We do NOT post-translate to 3.0; we target 3.1.
20
+ *
21
+ * 2. Effect Schema annotation surfacing: we walk the AST and, for each
22
+ * PropertySignature, read three saacms-namespaced annotations (`saacmsLabel`,
23
+ * `saacmsHelp`, `saacmsFormat`) plus the standard Effect annotations
24
+ * (`title`, `description`, `examples`). We project them onto the matching
25
+ * `properties[name]` slot in the emitted Schema Object. `saacmsLabel` wins
26
+ * over `title` for `description` (it's the human label intended for forms);
27
+ * `saacmsHelp` falls back to existing `description`; `saacmsFormat` overrides
28
+ * `format`.
29
+ *
30
+ * 3. `cookieAuth` and `bearerAuth` security schemes are *referenced* on every
31
+ * operation; their `components.securitySchemes` definitions are NOT emitted
32
+ * here. The runtime layer that assembles the full OpenAPI document attaches
33
+ * those scheme definitions (Better Auth cookie + JWT bearer per ADR 0008).
34
+ * TODO(runtime-assembler): wire securitySchemes when ADR 0008 lands.
35
+ *
36
+ * 4. Per ADR 0006, this generator emits the FULL spec for the Collection (every
37
+ * field, every verb). The runtime layer that returns `/api/openapi.json` per
38
+ * user calls into this output and then prunes routes/fields the user can't
39
+ * reach. The pruning is intentionally NOT done here so this projection stays
40
+ * a pure function of the Schema and CollectionDef (deterministic, cacheable,
41
+ * snapshot-testable).
42
+ *
43
+ * 5. Tag policy: one tag per Collection, equal to the Collection slug. Lets
44
+ * typed-client generators (`openapi-typescript`, `orval`, etc.) group SDK
45
+ * methods by Collection.
46
+ */
47
+ import { Schema } from "effect";
48
+ import type { CollectionDef, AnySchema } from "../schema/index.ts";
49
+ import type { OpenAPIPathsObject, OpenAPISchemaObject } from "./openapi-types.ts";
50
+ /**
51
+ * Pure pre-pass: returns a Schema equivalent to `schema` except every
52
+ * date-class field projects to `{ type: "string", format: "date-time" }`
53
+ * under `JSONSchema.make`. General over property path (top-level, nested
54
+ * struct, array items, union members, optional fields). Idempotent.
55
+ */
56
+ export declare function normalizeDateSchemas(schema: Schema.Schema<any, any, any>): Schema.Schema<any, any, any>;
57
+ /**
58
+ * Project an Effect Schema to an OpenAPI 3.1 Schema Object.
59
+ *
60
+ * Uses Effect's first-class JSON Schema export (`JSONSchema.make` with
61
+ * `target: "openApi3.1"`) and post-processes per-property to surface saacms
62
+ * UI annotations (label / help text / format) into OpenAPI fields.
63
+ *
64
+ * Date-class fields are normalized to the canonical RFC 3339 date-time string
65
+ * form first (see `normalizeDateSchemas`) so the projection is total for any
66
+ * valid schema per ADR 0005.
67
+ */
68
+ export declare function schemaToOpenApiSchema(schema: Schema.Schema<any, any, any>): OpenAPISchemaObject;
69
+ export interface CollectionToOpenApiPathsOptions {
70
+ /** Per ADR 0018 §13 — defaults to `/api/saacms/v1`. */
71
+ readonly basePath?: string;
72
+ }
73
+ export interface CollectionToOpenApiPathsResult {
74
+ readonly paths: OpenAPIPathsObject;
75
+ readonly components: {
76
+ readonly schemas: Record<string, OpenAPISchemaObject>;
77
+ };
78
+ }
79
+ export declare function collectionToOpenApiPaths(coll: CollectionDef<AnySchema>, opts?: CollectionToOpenApiPathsOptions): CollectionToOpenApiPathsResult;
80
+ //# sourceMappingURL=to-openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-openapi.d.ts","sourceRoot":"","sources":["../../src/codegen/to-openapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,EAAc,MAAM,EAAa,MAAM,QAAQ,CAAA;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,KAAK,EAEV,kBAAkB,EAKlB,mBAAmB,EAEpB,MAAM,oBAAoB,CAAA;AA4Q3B;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GACnC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAE9B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GACnC,mBAAmB,CAUrB;AA8GD,MAAM,WAAW,+BAA+B;IAC9C,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,8BAA8B;IAC7C,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,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,EAC9B,IAAI,GAAE,+BAAoC,GACzC,8BAA8B,CA+ChC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Schema → Puck field-config projection per ADR 0011 (the visual editor renders
3
+ * fields off a Puck `fields` config object generated at build time from the
4
+ * Effect Schema). One function:
5
+ *
6
+ * - `schemaToPuckFields(coll)` — walks the Collection's root Struct and
7
+ * returns a `{ <fieldName>: PuckField }` map.
8
+ *
9
+ * --------------------------------------------------------------------
10
+ * Annotation namespace (saacms-prefixed; mirrors `to-drizzle.ts` convention)
11
+ * --------------------------------------------------------------------
12
+ *
13
+ * Recognised on the per-property data-type AST node (read with
14
+ * `.annotations({ ... })` on the inner Schema):
15
+ *
16
+ * `saacmsLabel: string` — UI label override; otherwise the camelCase JS
17
+ * name is converted to Title Case (`publishedAt` → `Published At`).
18
+ *
19
+ * `saacmsMultiline: true` — narrows a `Schema.String` field from `text` to
20
+ * `textarea` in the Puck UI.
21
+ *
22
+ * `saacmsRadio: true` — switches a `Schema.Literal(...)` string-literal
23
+ * union from the default `select` to a `radio` field in the Puck UI.
24
+ *
25
+ * --------------------------------------------------------------------
26
+ * AST-walking decisions
27
+ * --------------------------------------------------------------------
28
+ *
29
+ * 1. The root schema MUST be a `TypeLiteral` (i.e. `Schema.Struct({...})`).
30
+ * Suspended / refined / union root schemas throw — Puck's `fields` config
31
+ * is shaped as a flat keyed map and presupposes a struct.
32
+ *
33
+ * 2. `Schema.optional(X)` and `Schema.Union(X, undefined|null)` are peeled
34
+ * when the only remaining non-nullable member is a single type. A
35
+ * string-literal union (`Literal("a") | Literal("b")`) is preserved as-is
36
+ * so the select/radio detection sees the full member set.
37
+ *
38
+ * Optionality is currently INVISIBLE to Puck at this layer per the brief —
39
+ * Puck treats every field as optional UI-wise; the Effect Schema enforces
40
+ * required-ness server-side on PUT/POST. So no marker is emitted.
41
+ *
42
+ * 3. `Refinement` wrappers (e.g. `Schema.minLength(1)`) are walked through —
43
+ * we look at `refinement.from` recursively until we hit a non-refinement.
44
+ * Puck has no constraint-validation hook at the field-config layer; the
45
+ * refinement still runs server-side via the Schema.
46
+ *
47
+ * 4. `Schema.Array(Schema.Struct({...}))` becomes `{ type: "array", arrayFields }`.
48
+ * `Schema.Array(scalar)` falls back to `{ type: "text" }` — Puck's array
49
+ * field requires sub-`fields` (record-shaped), and a primitive-array UI
50
+ * is deferred to v1.x.
51
+ *
52
+ * 5. Annotations live on the inner data-type node after union- and
53
+ * refinement-stripping. `Schema.optional(Schema.String.annotations({...}))`
54
+ * works identically to a non-optional annotated string.
55
+ *
56
+ * 6. Anything we don't recognise (union of non-literal types, arbitrary
57
+ * transformations, …) falls back to `{ type: "text" }`. The codegen-CLI
58
+ * surfaces this as a warning at build time.
59
+ */
60
+ import type { CollectionDef } from "../schema/index.ts";
61
+ export interface PuckFields {
62
+ readonly [name: string]: PuckField;
63
+ }
64
+ export type PuckField = {
65
+ readonly type: "text";
66
+ readonly label?: string;
67
+ } | {
68
+ readonly type: "textarea";
69
+ readonly label?: string;
70
+ } | {
71
+ readonly type: "number";
72
+ readonly label?: string;
73
+ } | {
74
+ readonly type: "select";
75
+ readonly label?: string;
76
+ readonly options: ReadonlyArray<{
77
+ readonly label: string;
78
+ readonly value: string;
79
+ }>;
80
+ } | {
81
+ readonly type: "radio";
82
+ readonly label?: string;
83
+ readonly options: ReadonlyArray<{
84
+ readonly label: string;
85
+ readonly value: string;
86
+ }>;
87
+ } | {
88
+ readonly type: "array";
89
+ readonly label?: string;
90
+ readonly arrayFields: PuckFields;
91
+ } | {
92
+ readonly type: "object";
93
+ readonly label?: string;
94
+ readonly objectFields: PuckFields;
95
+ };
96
+ /**
97
+ * camelCase → "Camel Case". Splits on uppercase letters, capitalizes the
98
+ * first letter of each token. `"title"` → `"Title"`, `"publishedAt"` →
99
+ * `"Published At"`.
100
+ */
101
+ export declare function defaultLabel(jsName: string): string;
102
+ /**
103
+ * Project a Collection's Effect Schema struct to a Puck `fields` config.
104
+ *
105
+ * Returns a `{ <fieldName>: PuckField }` map keyed by the JS property name
106
+ * (camelCase), one entry per top-level property of the root struct.
107
+ */
108
+ export declare function schemaToPuckFields(coll: CollectionDef): PuckFields;
109
+ //# sourceMappingURL=to-puck-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-puck-fields.d.ts","sourceRoot":"","sources":["../../src/codegen/to-puck-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAMvD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACpF,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACpF,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAA;CAAE,GACrF;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAA;CAAE,CAAA;AAwE3F;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOnD;AAkGD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,UAAU,CAWlE"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Schema → TypeScript client-types projection per ADR 0005 (Effect Schema is
3
+ * the single source of truth). Two functions:
4
+ *
5
+ * - `schemaToTsType(coll)` — projects ONE Collection's root Struct to an
6
+ * `export interface <PascalCaseSlug> { ... }` source string.
7
+ * - `schemaToTsTypes(colls)` — joins many with a blank line, prefixed once
8
+ * by a `// generated by saacms — do not edit` header.
9
+ *
10
+ * This is the hand-editable client-types artefact `saacms codegen` writes to
11
+ * `.saacms/types/<slug>.ts`. It agrees with `to-drizzle.ts` /
12
+ * `to-d1-migration.ts` on field names, auto-injected columns, and the order
13
+ * convention (id first, user fields in declared order, createdAt/updatedAt
14
+ * last).
15
+ *
16
+ * --------------------------------------------------------------------
17
+ * Annotation namespace (saacms-prefixed; mirrors `to-d1-migration.ts`)
18
+ * --------------------------------------------------------------------
19
+ *
20
+ * `saacmsTsName: string` — override the emitted TS property name (rare;
21
+ * for when the DB column and the TS field intentionally differ, e.g. an
22
+ * `authorId` column surfaced as `authorRef` client-side). Read on the
23
+ * inner data-type node after optional/refinement stripping.
24
+ *
25
+ * --------------------------------------------------------------------
26
+ * Type mapping (Effect Schema → TS source)
27
+ * --------------------------------------------------------------------
28
+ *
29
+ * - `Schema.String` → `string`
30
+ * - `Schema.Number` → `number`
31
+ * - `Schema.Boolean` → `boolean`
32
+ * - `Schema.Literal("a","b")` → `"a" | "b"`
33
+ * - `Schema.Date` / DateFromString → `string` (ISO-8601)
34
+ * - `Schema.Array(X)` → `<TS-of-X>[]` (recurse)
35
+ * - nested `Schema.Struct({...})` → inline `{ a: string; b: number }`
36
+ * - `Schema.optional(X)` / Union(X,⊥) → optional KEY (`name?: <TS-of-X>`),
37
+ * never `| undefined` in the value
38
+ * - `Union(X, null)` → `<TS-of-X> | null` (key NOT optional)
39
+ * - unrecognised shape → `unknown` (graceful fallback; the
40
+ * codegen-CLI surfaces a warning)
41
+ *
42
+ * Refinements (`Schema.minLength` etc.) are unwrapped to their base type — TS
43
+ * has no refinement types; the constraint still runs server-side via Schema.
44
+ */
45
+ import type { CollectionDef } from "../schema/index.ts";
46
+ /**
47
+ * Project a Collection's Effect Schema struct to an `export interface` source
48
+ * string. Auto-injected fields mirror `to-d1-migration.ts`: `id` first, then
49
+ * the user fields in declared order, then `createdAt` / `updatedAt` last (all
50
+ * ISO strings).
51
+ */
52
+ export declare function schemaToTsType(coll: CollectionDef): string;
53
+ /**
54
+ * Project an ordered list of Collections to a single TS source file: one
55
+ * `// generated by saacms — do not edit` header, then each interface
56
+ * separated by a blank line.
57
+ */
58
+ export declare function schemaToTsTypes(colls: ReadonlyArray<CollectionDef>): string;
59
+ //# sourceMappingURL=to-ts-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-ts-types.d.ts","sourceRoot":"","sources":["../../src/codegen/to-ts-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAuLvD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAyB1D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAClC,MAAM,CAER"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Effect-returning lifecycle hooks per ADR 0013.
3
+ *
4
+ * Hooks attach to Collections, Blocks, and Pages. Command-phase hooks (the
5
+ * `before*` family) can mutate data and cancel the operation via `Effect.fail`.
6
+ * Event-phase hooks (the `after*` family) run best-effort by default; failures
7
+ * are observable but non-blocking unless `failOnError` is set.
8
+ */
9
+ import type { Schema } from "effect";
10
+ import { Effect } from "effect";
11
+ import type { User } from "../types/user.ts";
12
+ export type CollectionHookMoment = "beforeValidate" | "beforeChange" | "afterChange" | "beforeRead" | "afterRead" | "beforeDelete" | "afterDelete" | "beforePublish" | "afterPublish";
13
+ export type BlockHookMoment = "beforeRender" | "afterRender";
14
+ export type PageHookMoment = "beforePublish" | "afterPublish";
15
+ export type HookMoment = CollectionHookMoment | BlockHookMoment | PageHookMoment;
16
+ export type HookOp = "create" | "update" | "delete" | "read" | "publish" | "render";
17
+ /**
18
+ * Argument passed to every hook. Typed against the target Collection/Block schema
19
+ * so the inferred `data` / `record` properties carry the right shape.
20
+ *
21
+ * For command-phase hooks `data` is the proposed new state; for event-phase
22
+ * hooks `record` is the state after the operation succeeded.
23
+ */
24
+ export interface HookContext<TSchema extends Schema.Schema<any, any, any>> {
25
+ readonly op: HookOp;
26
+ readonly user: User | null;
27
+ readonly data?: Schema.Schema.Type<TSchema>;
28
+ readonly record?: Schema.Schema.Type<TSchema>;
29
+ /** Free-form services container; populated by the runtime layer (Effect DI). */
30
+ readonly services?: Readonly<Record<string, unknown>>;
31
+ }
32
+ /**
33
+ * A hook is an Effect-returning function. The `R` (requirements) channel may
34
+ * carry user-declared service tags resolved by the runtime layer; we leave it
35
+ * `unknown` at the type boundary so both DI-using and DI-free hooks fit.
36
+ */
37
+ export type HookFn<TSchema extends Schema.Schema<any, any, any>> = (ctx: HookContext<TSchema>) => Effect.Effect<unknown, unknown, unknown>;
38
+ /**
39
+ * Per-hook configuration knobs (ADR 0013 §Shape).
40
+ * `failOnError` forces an `after*` hook to bubble failures to the caller.
41
+ * `queue` defers execution onto the platform queue (CF Queues / Vercel Queue).
42
+ */
43
+ export interface HookOptions {
44
+ readonly failOnError?: boolean;
45
+ readonly queue?: boolean;
46
+ }
47
+ /** A resolved hook entry: function plus optional per-hook configuration. */
48
+ export interface HookEntry<TSchema extends Schema.Schema<any, any, any>> {
49
+ readonly fn: HookFn<TSchema>;
50
+ readonly options?: HookOptions;
51
+ }
52
+ /**
53
+ * Run the resolved chain of hooks for a `moment` against `ctx`.
54
+ *
55
+ * `hooks` is pre-resolved (already filtered to the right moment, already in
56
+ * user-then-plugin order per ADR 0014 conflict resolution). Use
57
+ * {@link resolveHooksFor} to derive it from a `SaacmsConfig` slice.
58
+ *
59
+ * Command-phase moments (`before*`) compose sequentially via `Effect.flatMap`:
60
+ * any failure short-circuits and propagates; each hook sees a `ctx` whose
61
+ * `data` is the previous hook's success value (when that value is a
62
+ * non-undefined object), else the data is left untouched.
63
+ *
64
+ * Event-phase moments (`after*`) compose in parallel via `Effect.all` with
65
+ * `concurrency: "unbounded"`. Failures are warned to the console under the
66
+ * `[saacms/hooks]` prefix unless the entry sets `options.failOnError === true`,
67
+ * in which case the failure propagates.
68
+ *
69
+ * `options.queue: true` is a v1.x platform-queue marker — documented here but
70
+ * a no-op for this dispatch; `@saacms/plugin-queues` will pick this up and
71
+ * lift the hook onto the host's queue (CF Queues / Vercel Queue) in v1.x.
72
+ */
73
+ export declare function runHooks<TSchema extends Schema.Schema<any, any, any>>(moment: HookMoment, ctx: HookContext<TSchema>, hooks: ReadonlyArray<HookEntry<TSchema>>): Effect.Effect<unknown, unknown, unknown>;
74
+ /**
75
+ * Build the ordered hook chain for `moment` from a `SaacmsConfig` slice.
76
+ *
77
+ * Walks `source.collection.hooks[moment]` first, then each
78
+ * `source.plugins[i].hooks[moment]` in declaration order — matching ADR 0014
79
+ * §Conflict resolution (user wins, then plugins in listed order).
80
+ *
81
+ * Each `hooks[moment]` entry may be either a bare `HookFn` (wrapped as `{ fn }`)
82
+ * or a structured `{ fn, options? }` object. Anything else — strings, numbers,
83
+ * `undefined`, etc. — is silently skipped, since `hooks` is user-supplied data
84
+ * and a typo in a config file should not crash the runtime.
85
+ */
86
+ export declare function resolveHooksFor<TSchema extends Schema.Schema<any, any, any>>(moment: HookMoment, source: {
87
+ readonly collection?: {
88
+ readonly hooks?: Record<string, unknown>;
89
+ };
90
+ readonly plugins?: ReadonlyArray<{
91
+ readonly hooks?: Record<string, unknown>;
92
+ }>;
93
+ }): ReadonlyArray<HookEntry<TSchema>>;
94
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import {
2
+ resolveHooksFor,
3
+ runHooks
4
+ } from "../index-zgbq60fy.js";
5
+ export {
6
+ runHooks,
7
+ resolveHooksFor
8
+ };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Host adapter interface per ADRs 0001 (compile step + runtime), 0003 (per-host
3
+ * adapter responsibilities), and 0004 (per-block preview routes).
4
+ *
5
+ * One host adapter exists per host framework (`@saacms/host-nextjs`,
6
+ * `@saacms/host-sveltekit`, `@saacms/host-nuxt`, `@saacms/host-astro`). Each
7
+ * knows three things and only three things:
8
+ *
9
+ * 1. how to *generate* native route files in the host's idiom (compile step),
10
+ * 2. how to *mount* the saacms runtime handler at the host's API location,
11
+ * 3. how to *emit* per-block preview routes the Puck canvas can fetch.
12
+ *
13
+ * This interface is the seam at the Hosting bounded context (ADR 0020).
14
+ * Everything above it is host-agnostic; everything below it is host-specific.
15
+ */
16
+ import type { Hono } from "hono";
17
+ import type { PageDef, PageTemplateDef, BlockDef, RenderMode } from "../schema/index.ts";
18
+ /** Cookie names for the theme signal; constant so all host adapters are aligned. */
19
+ export declare const SCHEME_COOKIE: "saacms-scheme";
20
+ export declare const DARK_MODE_COOKIE: "saacms-dark";
21
+ /**
22
+ * No-FOUC theme-render contract (ADR 0033).
23
+ *
24
+ * A host adapter that supports themed SSR SHOULD implement this contract.
25
+ * saacms calls these methods during SSR to emit the active scheme's resolved
26
+ * token values into the initial document head before the body — zero flash,
27
+ * cache-correct.
28
+ *
29
+ * Contract only — concrete per-host emit is the named follow-up
30
+ * `theme-host-render`. An in-process conformance test proves correctness
31
+ * without a browser (same boundary discipline as the rest of the fleet).
32
+ */
33
+ export interface ThemeRenderContract {
34
+ /**
35
+ * Read the active scheme ID and dark-mode preference from the request.
36
+ * Reads `saacms-scheme` (scheme ID) and `saacms-dark` ("1" = dark) cookies
37
+ * server-side. Returns `{ schemeId: null, darkMode: false }` when no cookies
38
+ * are set — zero JavaScript required; the server reads directly from headers.
39
+ */
40
+ resolveActiveScheme(req: Request): {
41
+ schemeId: string | null;
42
+ darkMode: boolean;
43
+ };
44
+ /**
45
+ * Emit the scheme's resolved token values as an inline `<style>` block
46
+ * targeting `:root`. The returned HTML string is injected into the document
47
+ * `<head>` before any external stylesheets so cascade order is preserved.
48
+ *
49
+ * @param tokens — map of CSS custom-property name → value (without `--` prefix).
50
+ */
51
+ emitThemeStyles(tokens: Record<string, string>): string;
52
+ /**
53
+ * Emit attribute markup for the document root element that identifies the
54
+ * active scheme and dark mode. Returns a string of HTML attributes
55
+ * (e.g. `data-theme="ocean" class="dark"`) to be placed on the `<html>` tag
56
+ * pre-body — the browser renders with the correct theme on the first paint.
57
+ */
58
+ emitThemeAttribute(schemeId: string, darkMode: boolean): string;
59
+ }
60
+ export interface GeneratedRoute {
61
+ /** Repo-relative output path the host expects (e.g. `app/blog/[slug]/page.tsx`). */
62
+ readonly path: string;
63
+ readonly contents: string;
64
+ /** Per ADR 0021 — the cache emitter records mode metadata for cache wiring. */
65
+ readonly mode?: RenderMode;
66
+ }
67
+ export interface GenerateRouteContext {
68
+ readonly page: PageDef | PageTemplateDef;
69
+ /** When the page is a template, the bound Collection slug. */
70
+ readonly boundCollection?: string;
71
+ /** Repo-absolute path to the host's route root (`app/`, `src/routes/`, …). */
72
+ readonly routeRoot: string;
73
+ }
74
+ export interface GeneratePreviewRouteContext {
75
+ readonly block: BlockDef;
76
+ /** Repo-absolute path to the host's route root. */
77
+ readonly routeRoot: string;
78
+ }
79
+ /**
80
+ * Returned from `mountTemplate` so `saacms init` can write the right ~3-line
81
+ * mount file in the host's conventional location (e.g.
82
+ * `app/api/saacms/[...slug]/route.ts` for Next.js).
83
+ */
84
+ export interface MountTemplate {
85
+ /** Repo-relative path of the mount file the host adapter expects. */
86
+ readonly path: string;
87
+ /** File contents — typically a tiny re-export of the runtime handler. */
88
+ readonly contents: string;
89
+ }
90
+ export interface HostAdapter {
91
+ readonly name: string;
92
+ /** Generate native route file(s) for a Page or Page template. */
93
+ generateRoute(ctx: GenerateRouteContext): ReadonlyArray<GeneratedRoute>;
94
+ /**
95
+ * Generate the per-Block preview route the Puck canvas fetches in dev mode
96
+ * (ADR 0004). Returns `null` if the block doesn't need a preview route
97
+ * (e.g. WC-mode blocks that render entirely client-side).
98
+ */
99
+ generatePreviewRoute(ctx: GeneratePreviewRouteContext): GeneratedRoute | null;
100
+ /** Repo-relative directory the host serves static assets from (`public/` / `static/`). */
101
+ readonly assetRoot: string;
102
+ /**
103
+ * Returns the mount-file template `saacms init` writes into the host repo.
104
+ * Receives the runtime app so the adapter can decide whether to import it
105
+ * lazily, wrap it in framework-specific request adapters, etc.
106
+ */
107
+ mountTemplate(runtime: Hono): MountTemplate;
108
+ }
109
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAMxF,oFAAoF;AACpF,eAAO,MAAM,aAAa,EAAG,eAAwB,CAAA;AACrD,eAAO,MAAM,gBAAgB,EAAG,aAAsB,CAAA;AAEtD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAEjF;;;;;;OAMG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAEvD;;;;;OAKG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;CAChE;AAMD,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAA;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,CAAA;IACxC,8DAA8D;IAC9D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;IACxB,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAMD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAMD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB,iEAAiE;IACjE,aAAa,CAAC,GAAG,EAAE,oBAAoB,GAAG,aAAa,CAAC,cAAc,CAAC,CAAA;IAEvE;;;;OAIG;IACH,oBAAoB,CAAC,GAAG,EAAE,2BAA2B,GAAG,cAAc,GAAG,IAAI,CAAA;IAE7E,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAE1B;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,IAAI,GAAG,aAAa,CAAA;CAC5C"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Host adapter interface per ADRs 0001 (compile step + runtime), 0003 (per-host
3
+ * adapter responsibilities), and 0004 (per-block preview routes).
4
+ *
5
+ * One host adapter exists per host framework (`@saacms/host-nextjs`,
6
+ * `@saacms/host-sveltekit`, `@saacms/host-nuxt`, `@saacms/host-astro`). Each
7
+ * knows three things and only three things:
8
+ *
9
+ * 1. how to *generate* native route files in the host's idiom (compile step),
10
+ * 2. how to *mount* the saacms runtime handler at the host's API location,
11
+ * 3. how to *emit* per-block preview routes the Puck canvas can fetch.
12
+ *
13
+ * This interface is the seam at the Hosting bounded context (ADR 0020).
14
+ * Everything above it is host-agnostic; everything below it is host-specific.
15
+ */
16
+ export {};
@@ -0,0 +1,4 @@
1
+ // src/signals/index.ts
2
+ import { signal, computed, effect } from "@preact/signals-core";
3
+
4
+ export { signal, computed, effect };