@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,49 @@
1
+ /**
2
+ * `/api/saacms/v1/schemes` — Scheme CRUD (ADR 0033, Operating law Zone 1).
3
+ *
4
+ * A Scheme is a runtime-created, Owner-saved named set of values for an
5
+ * existing Theme's token contract — the presentation analogue of a Pattern.
6
+ * It is NOT a dev-authored primitive (there is deliberately no `defineScheme`);
7
+ * it is stored *content*, persisted through the same `RowStorageAdapter` as
8
+ * Collection records — here against a reserved table slug `"saacms_schemes"`.
9
+ *
10
+ * Structurally mirrors `pattern-route.ts` exactly: shared `problemDetails`
11
+ * (RFC 9457), `resolveCache` read-through + tag invalidation (ADR 0021 §6),
12
+ * `resolveHooksFor` / `runHooks` event-phase dispatch (ADR 0013), HATEOAS
13
+ * envelope, and weak-ETag / `If-None-Match` / `If-Match` semantics.
14
+ *
15
+ * Scheme-specific additions beyond the Pattern contract:
16
+ *
17
+ * - **Token validation.** When `config.theme` is set, `tokens` and
18
+ * `darkTokens` keys are validated against the declared token contract. An
19
+ * unknown token name → 422. Missing dark values are permitted (light used
20
+ * as fallback — documented behaviour, not rejected in v1).
21
+ * - **No theme → no validation.** When `config.theme` is absent the route
22
+ * still functions; token validation is skipped (no contract to validate).
23
+ * - **No-auto-publish.** Saving a Scheme is Zone-1 self-serve and MUST NOT
24
+ * trigger Publish / git commit / CI.
25
+ * - **Access (v1, same rule as Patterns).** Writes (POST/DELETE) require a
26
+ * logged-in user; reads are open.
27
+ */
28
+ import type { Env, Hono } from "hono";
29
+ import type { SaacmsConfig } from "../schema/index.ts";
30
+ /**
31
+ * The persisted Scheme shape. `tokens` maps CSS custom-property names (without
32
+ * `--` prefix) to their string values. `darkTokens` carries the dark-mode
33
+ * overrides for tokens that declare `dark: true` in the Theme contract.
34
+ */
35
+ export interface SchemeRecord {
36
+ id: string;
37
+ /** Human label, required, 1..120 chars. */
38
+ name: string;
39
+ /** CSS token name → value map (without `--` prefix). */
40
+ tokens: Record<string, string>;
41
+ /** Dark-variant override for tokens declared with `dark: true`. May be absent. */
42
+ darkTokens?: Record<string, string>;
43
+ /** User id of the Owner/editor who saved it; `null` if anon-disabled. */
44
+ createdBy: string | null;
45
+ createdAt: string;
46
+ updatedAt: string;
47
+ }
48
+ export declare function mountSchemeRoute<E extends Env>(app: Hono<E>, config: SaacmsConfig): void;
49
+ //# sourceMappingURL=scheme-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheme-route.d.ts","sourceRoot":"","sources":["../../src/runtime/scheme-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAW,GAAG,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG9C,OAAO,KAAK,EAAa,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAoBjE;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAA;IACZ,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,yEAAyE;IACzE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAC5C,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,EACZ,MAAM,EAAE,YAAY,GACnB,IAAI,CAwON"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Plugin-services registry.
3
+ *
4
+ * Per ADR 0014, plugins may publish service handles via `PluginDef.services`
5
+ * (e.g. `@saacms/plugin-cache-kv` → `services.cache`, `@saacms/plugin-realtime`
6
+ * → `services.realtime`). Route handlers need to reach these without every
7
+ * `mountXRoute(app, config)` growing a new parameter, so the runtime folds them
8
+ * into one `SaacmsServices` object at app-build time and a single `app.use`
9
+ * middleware stashes it on the Hono context (key `"saacmsServices"`).
10
+ *
11
+ * Handlers read it back via `getServices(c)` — the same cast-through-`unknown`
12
+ * idiom `auth-middleware` uses for `c.get("user")`.
13
+ */
14
+ import type { Context } from "hono";
15
+ import type { SaacmsConfig } from "../schema/index.ts";
16
+ /**
17
+ * The merged set of plugin-contributed service handles. Known plugins document
18
+ * their well-known keys here; the index signature keeps it forward-compatible
19
+ * for plugins this package doesn't know about.
20
+ */
21
+ export interface SaacmsServices {
22
+ readonly cache?: unknown;
23
+ readonly realtime?: unknown;
24
+ readonly [k: string]: unknown;
25
+ }
26
+ /**
27
+ * Fold every `config.plugins[i].services` object left-to-right into one frozen
28
+ * `SaacmsServices`. Plugins without a `services` field are skipped.
29
+ *
30
+ * Ordering (per ADR 0014): the user registers plugins in priority order, so a
31
+ * later plugin is the more specific one — on a key collision the LATER plugin
32
+ * wins (last-wins). The returned object is frozen so handlers can't mutate the
33
+ * shared registry mid-request.
34
+ */
35
+ export declare function collectServices(config: SaacmsConfig): SaacmsServices;
36
+ /**
37
+ * Read the services registry the middleware stashed onto the context. Returns
38
+ * an empty frozen object when unset so a route never crashes merely because no
39
+ * plugin registered services.
40
+ */
41
+ export declare function getServices(c: Context): SaacmsServices;
42
+ //# sourceMappingURL=services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/runtime/services.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAKtD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC9B;AAID;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CAWpE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,cAAc,CAKtD"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `PATCH /api/saacms/v1/:collection/:id` — partial-update endpoint.
3
+ *
4
+ * Per RFC 7396 (JSON Merge Patch) the body is a partial document — keys
5
+ * present replace the same key on the record, `null` sets the key to `null`,
6
+ * and absent keys are left untouched. v1 ships a **shallow** merge only;
7
+ * the response `_meta.mergeStyle` is set to `"shallow-rfc7396"` so callers
8
+ * can detect this rather than guessing.
9
+ *
10
+ * Optimistic concurrency follows RFC 9110 §13 — when the request supplies
11
+ * `If-Match`, we compute the same weak ETag the read route emits and
12
+ * short-circuit to `412` on mismatch. Validation runs on the *merged*
13
+ * record (Effect `Schema.decodeUnknownEither`) so partial updates can't
14
+ * cross the integrity envelope of the Collection schema. Errors are
15
+ * RFC 9457 Problem Details per ADR 0018 §3.
16
+ */
17
+ import type { Env, Hono } from "hono";
18
+ import type { SaacmsConfig } from "../schema/index.ts";
19
+ export declare function mountUpdateRoute<E extends Env>(app: Hono<E>, config: SaacmsConfig): void;
20
+ //# sourceMappingURL=update-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-route.d.ts","sourceRoot":"","sources":["../../src/runtime/update-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAW,GAAG,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAK9C,OAAO,KAAK,EAA4B,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAsBhF,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAC5C,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,EACZ,MAAM,EAAE,YAAY,GACnB,IAAI,CAmTN"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `POST /api/saacms/v1/:collection:upload` — bucket-mode Media upload action.
3
+ *
4
+ * This is the entry point the ADR-0024 v1.0 **Media** gate requires: it
5
+ * accepts a `multipart/form-data` file, stores the bytes via the configured
6
+ * binary `StorageAdapter` (`config.storage.media`), builds the ADR-0009
7
+ * auto-injected media field set, persists the record via the row store, and
8
+ * returns a `201 Created` HAL-light envelope whose `data` is a
9
+ * `ResolvedMediaRef`-compatible record (so a consumer can call
10
+ * `resolveMediaRef(record).url({...})`).
11
+ *
12
+ * It is registered as a **custom action** (the colon-verb idiom of ADR 0018
13
+ * §"Custom action" / Google AIP-136) so it does NOT collide with
14
+ * `create-route`'s `POST /api/saacms/v1/:collection` JSON handler — that route
15
+ * speaks `application/json`, this one speaks `multipart/form-data`. Hono
16
+ * cannot express `:collection:upload` as a bare path param (its segment-param
17
+ * grammar, `^:([^{}]+)(?:{(.+)})?$`, would capture the literal `:upload` into
18
+ * the param name), so the route is mounted with an explicit regexp param —
19
+ * `:collectionAction{[^/]+:upload}` — that matches the whole
20
+ * `<slug>:upload` segment; the handler strips the `:upload` suffix to recover
21
+ * the Collection slug. This preserves the ADR-0018 URL shape verbatim.
22
+ *
23
+ * ROUTING ORDER (see `index.ts`): the regexp param `[^/]+` also matches
24
+ * `posts:upload`, so `create-route`'s `:collection` param would happily bind
25
+ * `collection = "posts:upload"` if it were matched first. Hono runs matched
26
+ * handlers in registration order and the first to return a Response wins, so
27
+ * `mountUploadRoute` MUST be registered BEFORE `mountCreateRoute`. A plain
28
+ * `POST /api/saacms/v1/posts` never matches this route (no `:upload` suffix)
29
+ * and correctly falls through to `create-route`. Verified empirically +
30
+ * by the sibling test's create-still-works scenario.
31
+ *
32
+ * Per ADR 0021 §6 a successful write carries `Cache-Control: no-store`.
33
+ * Every 4xx/5xx response is an RFC 9457 `application/problem+json` document
34
+ * via the shared `problemDetails` helper.
35
+ *
36
+ * SCOPE: bucket-mode only (ADR 0024). Repo-mode media (commit-on-publish) is
37
+ * a later wave. There is no `storage`-mode discriminant on `CollectionDef`
38
+ * yet (see `schema/media.ts` header), so a repo-mode Collection is not
39
+ * distinguishable here; the de-facto signal that bucket storage is not
40
+ * available is an unwired `config.storage.media` adapter, which returns a
41
+ * misconfigured Problem-Details pointing the host at the fix.
42
+ */
43
+ import type { Env, Hono } from "hono";
44
+ import type { SaacmsConfig } from "../schema/index.ts";
45
+ export declare function mountUploadRoute<E extends Env>(app: Hono<E>, config: SaacmsConfig): void;
46
+ //# sourceMappingURL=upload-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-route.d.ts","sourceRoot":"","sources":["../../src/runtime/upload-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAIH,OAAO,KAAK,EAAW,GAAG,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE9C,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,oBAAoB,CAAA;AA+BrE,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAC5C,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,EACZ,MAAM,EAAE,YAAY,GACnB,IAAI,CA8PN"}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Schema definition surface — `defineCollection`, `definePage`, `definePageTemplate`,
3
+ * `defineBlock`, `definePlugin`, `defineConfig`.
4
+ *
5
+ * Per ADR 0005 (Effect Schema is the single source of truth) every Collection and Block
6
+ * carries an `effect/Schema` definition; saacms generates the Drizzle table, OpenAPI
7
+ * Schema Object, TS types, and Puck field config from it.
8
+ *
9
+ * v1 alpha: these helpers are identity functions over typed options. They exist so
10
+ * downstream code can hold typed references (`typeof Posts.schema`) and so the
11
+ * generators have a single canonical shape to walk. Real validation, registration
12
+ * ordering, and conflict detection (per ADR 0014) land with the runtime impl.
13
+ */
14
+ import type { Schema } from "effect";
15
+ import type { CollectionSlug, BlockSlug, PageID } from "../types/ids.ts";
16
+ import type { Role } from "../types/user.ts";
17
+ import type { AccessVerb, AccessPredicate } from "../access/index.ts";
18
+ import type { AuthAdapter } from "../auth/index.ts";
19
+ import type { SaacmsStorageConfig } from "../storage/index.ts";
20
+ import type { PluginCapability } from "./plugin-trust.ts";
21
+ import type { ThemeDef } from "../theme/index.ts";
22
+ export type { ThemeDef } from "../theme/index.ts";
23
+ /**
24
+ * An Effect Schema for a record/block payload.
25
+ *
26
+ * We accept any Effect Schema; downstream code recovers the inferred type via
27
+ * `Schema.Schema.Type<typeof X.schema>`.
28
+ */
29
+ export type AnySchema = Schema.Schema<any, any, any>;
30
+ /**
31
+ * Per ADR 0006 §1 — Collection-level access is a partial record of per-verb
32
+ * predicates. Predicate types live in `../access/` to avoid duplication.
33
+ */
34
+ export type CollectionAccess<TRecord = unknown> = Partial<Record<AccessVerb, AccessPredicate<TRecord>>>;
35
+ /** Per ADR 0009 — `kind: "media"` Collections get auto-injected fields. */
36
+ export type CollectionKind = "data" | "media";
37
+ export interface CollectionDef<TSchema extends AnySchema = AnySchema> {
38
+ readonly slug: CollectionSlug | string;
39
+ readonly schema: TSchema;
40
+ readonly kind?: CollectionKind;
41
+ readonly access?: CollectionAccess<Schema.Schema.Type<TSchema>>;
42
+ /** Per ADR 0013 — Effect-returning lifecycle functions, keyed by moment. */
43
+ readonly hooks?: Record<string, unknown>;
44
+ /** Optional human-readable label for the admin UI. */
45
+ readonly label?: string;
46
+ /** Optional sort/filter declarations surfaced into OpenAPI per ADR 0018 §5–6. */
47
+ readonly sortable?: ReadonlyArray<string>;
48
+ readonly filterable?: ReadonlyArray<string>;
49
+ /**
50
+ * Per ADR 0030 — declarative dedup primitive. Each inner array names a set
51
+ * of camelCase domain fields that must be collectively unique across all rows
52
+ * in the collection. The codegen emits a `UNIQUE(...)` table constraint for
53
+ * each group; a violation → 409 Conflict (no `afterChange` fired, no second row).
54
+ *
55
+ * Additive optional: omitting it leaves existing collections byte-identical.
56
+ */
57
+ readonly unique?: ReadonlyArray<ReadonlyArray<string>>;
58
+ /**
59
+ * Per ADR 0009 — storage mode for `kind: "media"` Collections.
60
+ * Storage mode is a per-Media-Collection choice (ADR 0009 §"storage mode is a
61
+ * per-Media-Collection choice"). Ignored for `kind: "data"` / kind-less Collections.
62
+ *
63
+ * - `"bucket"` (default when omitted): binary in object storage (R2/Blob/S3);
64
+ * injects `originalKey` + `originalUrl`; on-demand CF/Vercel transforms.
65
+ * - `"repo"`: binary committed to the host's static-asset folder on Publish;
66
+ * injects `repoPath` + `publicUrl`; transforms via the host framework's build pipeline.
67
+ */
68
+ readonly storage?: {
69
+ readonly mode: "bucket" | "repo";
70
+ readonly naming?: "content-hash" | "slug";
71
+ };
72
+ }
73
+ export declare function defineCollection<TSchema extends AnySchema>(opts: CollectionDef<TSchema>): CollectionDef<TSchema>;
74
+ /** Per ADR 0007 / 0021 — render mode is an attribute of the route or resource. */
75
+ export type RenderMode = "static" | "isr" | "dynamic";
76
+ export interface PageDef {
77
+ readonly id: PageID | string;
78
+ /** URL pattern. For a plain Page this is a literal path (`/about`). */
79
+ readonly path: string;
80
+ readonly renderMode?: RenderMode;
81
+ /** Stored Puck tree JSON. Opaque to the type system; validated at runtime. */
82
+ readonly layout?: unknown;
83
+ readonly hooks?: Record<string, unknown>;
84
+ }
85
+ export declare function definePage(opts: PageDef): PageDef;
86
+ export interface PageTemplateDef {
87
+ readonly id: PageID | string;
88
+ /** URL pattern with one or more params (`/blog/[slug]`). */
89
+ readonly path: string;
90
+ readonly renderMode: RenderMode;
91
+ /** Slug of the Collection this template binds to. Per ADR 0010. */
92
+ readonly collection: CollectionSlug | string;
93
+ /** URL param → record-field mapping (e.g. `{ slug: "slug" }`). */
94
+ readonly params: Readonly<Record<string, string>>;
95
+ readonly layout?: unknown;
96
+ readonly hooks?: Record<string, unknown>;
97
+ }
98
+ export declare function definePageTemplate(opts: PageTemplateDef): PageTemplateDef;
99
+ /** Per ADR 0004 — Block authoring is per-block: WC or framework-native. */
100
+ export type BlockAuthoringMode = "web-component" | "framework-native";
101
+ export interface BlockDef<TSchema extends AnySchema = AnySchema> {
102
+ readonly slug: BlockSlug | string;
103
+ readonly schema: TSchema;
104
+ readonly mode: BlockAuthoringMode;
105
+ /**
106
+ * For `web-component` mode: the custom-element tag name.
107
+ * For `framework-native` mode: the import specifier of the component module.
108
+ */
109
+ readonly component: string;
110
+ /** Per ADR 0013 — `beforeRender` / `afterRender`. */
111
+ readonly hooks?: Record<string, unknown>;
112
+ /** Per migration ADR — bumped each time a content migration is generated. */
113
+ readonly version?: number;
114
+ }
115
+ export declare function defineBlock<TSchema extends AnySchema>(opts: BlockDef<TSchema>): BlockDef<TSchema>;
116
+ export interface PluginDef {
117
+ readonly name: string;
118
+ readonly version: string;
119
+ readonly collections?: ReadonlyArray<CollectionDef>;
120
+ readonly blocks?: ReadonlyArray<BlockDef>;
121
+ readonly pages?: ReadonlyArray<PageDef>;
122
+ readonly pageTemplates?: ReadonlyArray<PageTemplateDef>;
123
+ /** Plugin routes mounted under `/api/saacms/<plugin-name>/...`. */
124
+ readonly routes?: Record<string, unknown>;
125
+ /** Hooks keyed by `"<CollectionSlug>.<moment>"`. */
126
+ readonly hooks?: Record<string, unknown>;
127
+ /** Effect service factories the plugin publishes. */
128
+ readonly services?: Record<string, unknown>;
129
+ readonly admin?: {
130
+ readonly pages?: ReadonlyArray<{
131
+ readonly slug: string;
132
+ readonly title: string;
133
+ readonly component: string;
134
+ }>;
135
+ readonly menu?: ReadonlyArray<unknown>;
136
+ };
137
+ readonly migrations?: {
138
+ readonly db?: string;
139
+ readonly content?: string;
140
+ };
141
+ /** One-shot install Effect (e.g. provision external webhook). */
142
+ readonly init?: unknown;
143
+ /**
144
+ * Per ADR 0029 — OPTIONAL declared capability surface. When present, CI
145
+ * (`assertPluginCapabilities`) flags any contributed capability the plugin
146
+ * did not declare. Absent ⇒ unchanged behaviour (additive, back-compat);
147
+ * the plugin is merely listed as undeclared. Detection, not enforcement —
148
+ * v1 plugins remain fully trusted (ADR 0027 §6).
149
+ */
150
+ readonly capabilities?: ReadonlyArray<PluginCapability>;
151
+ }
152
+ export declare function definePlugin(opts: PluginDef): PluginDef;
153
+ export interface SaacmsConfig {
154
+ readonly collections?: ReadonlyArray<CollectionDef>;
155
+ readonly blocks?: ReadonlyArray<BlockDef>;
156
+ readonly pages?: ReadonlyArray<PageDef>;
157
+ readonly pageTemplates?: ReadonlyArray<PageTemplateDef>;
158
+ readonly plugins?: ReadonlyArray<PluginDef>;
159
+ /** Per ADR 0008 — devs may extend the role enum. */
160
+ readonly roles?: ReadonlyArray<Role>;
161
+ /** Mount path for the runtime handler; defaults to `/api/saacms`. */
162
+ readonly mountPath?: string;
163
+ /** OpenAPI `info.title`. Surfaced in `/api/saacms/v1/openapi.json`. */
164
+ readonly title?: string;
165
+ /** OpenAPI `info.version` — semver of the host app's API. */
166
+ readonly version?: string;
167
+ /** OpenAPI `info.description`. Optional free-form summary. */
168
+ readonly description?: string;
169
+ /** Free-form host adapter handles, validated by their own packages. */
170
+ readonly host?: unknown;
171
+ /** Per ADR 0020 — row + media storage adapters injected by the host. */
172
+ readonly storage?: SaacmsStorageConfig;
173
+ /** Per ADR 0008 — auth adapter the host wires (e.g. a Better Auth shim). */
174
+ readonly auth?: AuthAdapter;
175
+ /**
176
+ * Per ADR 0033 — optional Developer-authored token contract. When absent no
177
+ * new behaviour is introduced and all existing routes + cache keys are
178
+ * byte-identical (additive optional).
179
+ */
180
+ readonly theme?: ThemeDef;
181
+ }
182
+ export declare function defineConfig(opts: SaacmsConfig): SaacmsConfig;
183
+ export * from "./media.ts";
184
+ export * from "./plugin-trust.ts";
185
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import {
2
+ MEDIA_FIELD_NAMES,
3
+ MediaFields,
4
+ MediaRef,
5
+ PLUGIN_CAPABILITIES,
6
+ REPO_MEDIA_FIELD_NAMES,
7
+ assertPluginCapabilities,
8
+ defineBlock,
9
+ defineCollection,
10
+ defineConfig,
11
+ definePage,
12
+ definePageTemplate,
13
+ definePlugin,
14
+ deriveContributedCapabilities,
15
+ mediaRepoName,
16
+ mediaTransformUrl,
17
+ resolveMediaRef,
18
+ stageRepoMedia,
19
+ withMediaFields
20
+ } from "../index-8g8ymd37.js";
21
+ export {
22
+ withMediaFields,
23
+ stageRepoMedia,
24
+ resolveMediaRef,
25
+ mediaTransformUrl,
26
+ mediaRepoName,
27
+ deriveContributedCapabilities,
28
+ definePlugin,
29
+ definePageTemplate,
30
+ definePage,
31
+ defineConfig,
32
+ defineCollection,
33
+ defineBlock,
34
+ assertPluginCapabilities,
35
+ REPO_MEDIA_FIELD_NAMES,
36
+ PLUGIN_CAPABILITIES,
37
+ MediaRef,
38
+ MediaFields,
39
+ MEDIA_FIELD_NAMES
40
+ };
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Media as a Collection kind — auto-injected field sets (bucket + repo mode),
3
+ * the `MediaRef` reference constructor, the pure Cloudflare Image Transformations
4
+ * URL builder, the repo-mode naming policy, and the repo staging primitive.
5
+ *
6
+ * Per ADR 0009 (Media is a Collection with `kind: "media"`) and
7
+ * ADR 0024 (v1 = bucket mode + R2 + on-demand CF transforms; repo mode data
8
+ * model delivered in this wave as the primitive the Publish pipeline will call).
9
+ *
10
+ * Everything here is a PURE function of its inputs — no I/O, no network, except
11
+ * `mediaRepoName` and `stageRepoMedia` which invoke Web Crypto `crypto.subtle.digest`
12
+ * (async, deterministic, no network). The transform URL is deterministic string
13
+ * building; the field injection is a pure AST rewrite mirroring the `to-openapi.ts`
14
+ * `Schema.make(ast)` idiom.
15
+ *
16
+ * ----------------------------------------------------------------------------
17
+ * Auto-injected field set (ADR 0009 §1, lines 102–105)
18
+ * ----------------------------------------------------------------------------
19
+ *
20
+ * `kind: "media"` adds these regardless of storage mode:
21
+ * - `mime` string — content type (ADR 0009:102)
22
+ * - `size` int — bytes (ADR 0009:102)
23
+ * - `width` int — pixels, where applicable (ADR 0009:102)
24
+ * - `height` int — pixels, where applicable (ADR 0009:102)
25
+ * - `uploadedBy` string — user ref (ADR 0009:102)
26
+ *
27
+ * Bucket mode (default) also injects (ADR 0009:104):
28
+ * - `originalKey` string — object storage key
29
+ * - `originalUrl` string — canonical URL
30
+ *
31
+ * Repo mode injects instead (ADR 0009:105):
32
+ * - `repoPath` string — path inside the repo, e.g. `public/saacms/blog/abc1234.png`
33
+ * - `publicUrl` string — path the host framework serves, e.g. `/saacms/blog/abc1234.png`
34
+ *
35
+ * `int` fields carry the `saacmsInt: true` saacms annotation so the D1
36
+ * projection emits `INTEGER` rather than `REAL`.
37
+ *
38
+ * ----------------------------------------------------------------------------
39
+ * Reserved-name rules
40
+ * ----------------------------------------------------------------------------
41
+ *
42
+ * For a bucket (or default) collection: the 7 bucket-set names are reserved
43
+ * (mime, size, width, height, uploadedBy, originalKey, originalUrl).
44
+ * For a repo collection: the 7 repo-set names are reserved
45
+ * (mime, size, width, height, uploadedBy, repoPath, publicUrl).
46
+ * A collection of one mode does NOT reserve the other mode's specific names
47
+ * (a repo collection may declare an `originalKey` author field if it needs one,
48
+ * and a bucket collection may declare `repoPath` — neither makes sense in
49
+ * practice, but the schema language does not forbid cross-mode author names).
50
+ */
51
+ import { Schema } from "effect";
52
+ import type { CollectionDef } from "./index.ts";
53
+ /**
54
+ * The ADR-0009 auto-injected media fields as an Effect Schema struct (bucket set).
55
+ * Exported for backward compatibility and for introspection by the four projections.
56
+ * Repo-mode callers use `_RepoOnlyFields` + `_CommonMediaFields` internally; the
57
+ * individual field signatures are accessed through the mode-aware injection path.
58
+ */
59
+ export declare const MediaFields: Schema.Struct<{
60
+ mime: typeof Schema.String;
61
+ size: Schema.SchemaClass<number, number, never>;
62
+ width: Schema.SchemaClass<number, number, never>;
63
+ height: Schema.SchemaClass<number, number, never>;
64
+ uploadedBy: typeof Schema.String;
65
+ originalKey: typeof Schema.String;
66
+ originalUrl: typeof Schema.String;
67
+ }>;
68
+ /**
69
+ * Reserved injected names for bucket mode — author re-declaration of any is an
70
+ * error. Exported as `MEDIA_FIELD_NAMES` for backward compatibility (default
71
+ * mode is bucket; the name predates the mode split).
72
+ */
73
+ export declare const MEDIA_FIELD_NAMES: ReadonlyArray<string>;
74
+ /**
75
+ * Reserved injected names for repo mode. A repo collection re-declaring any of
76
+ * these throws a precise reserved-field error.
77
+ */
78
+ export declare const REPO_MEDIA_FIELD_NAMES: ReadonlyArray<string>;
79
+ /**
80
+ * Project a Collection to the Collection the generators should actually see.
81
+ *
82
+ * - `coll.kind !== "media"` → returns `coll` UNCHANGED (referential identity
83
+ * preserved; a `data` / kind-less Collection is byte-identical downstream).
84
+ * - `coll.kind === "media"` → returns a NEW `CollectionDef` whose `.schema`
85
+ * is the author's Effect Schema struct with the ADR-0009 fields appended.
86
+ * The mode is read from `coll.storage?.mode` (default `"bucket"`).
87
+ * The input is never mutated.
88
+ *
89
+ * Reserved-name collision throws a precise `Error` referencing the mode-specific
90
+ * reserved set (a repo collection re-declaring `repoPath` throws; a bucket
91
+ * collection re-declaring `originalUrl` throws; cross-mode names are NOT reserved
92
+ * — see module header for the rationale).
93
+ *
94
+ * Implementation is a pure AST rewrite (`Schema.make` over a freshly built
95
+ * `TypeLiteral`), the same idiom `to-openapi.ts` uses for date normalization.
96
+ */
97
+ export declare function withMediaFields(coll: CollectionDef): CollectionDef;
98
+ /**
99
+ * Resolved shape for a bucket-mode `MediaRef`. Carries the canonical object
100
+ * storage URL that `.url(transform?)` passes to `mediaTransformUrl`.
101
+ */
102
+ export interface BucketResolvedMediaRef {
103
+ readonly id: string;
104
+ readonly originalUrl: string;
105
+ readonly mime: string;
106
+ readonly width: number;
107
+ readonly height: number;
108
+ }
109
+ /**
110
+ * Resolved shape for a repo-mode `MediaRef`. Carries the static `publicUrl`
111
+ * (the path the host framework serves) and `repoPath` (where the binary was
112
+ * committed). `.url()` returns `publicUrl` unchanged — repo-mode transforms
113
+ * are the host framework's build-time job (`<Image>` / `next/image` / etc.),
114
+ * not a CF on-demand URL.
115
+ */
116
+ export interface RepoResolvedMediaRef {
117
+ readonly id: string;
118
+ readonly publicUrl: string;
119
+ readonly repoPath: string;
120
+ readonly mime: string;
121
+ readonly width: number;
122
+ readonly height: number;
123
+ }
124
+ /**
125
+ * Mode-agnostic resolved media ref (ADR 0009 §6). Either a bucket record
126
+ * (has `originalUrl`) or a repo record (has `publicUrl` + `repoPath`).
127
+ * Consuming code calls `.url()` on the `MediaRefAccessor` and never needs
128
+ * to branch on the mode directly.
129
+ */
130
+ export type ResolvedMediaRef = BucketResolvedMediaRef | RepoResolvedMediaRef;
131
+ /**
132
+ * A saacms `MediaRef` Schema constructor (ADR 0009 §2). Produces an Effect
133
+ * Schema for a typed reference to a media record.
134
+ *
135
+ * `slug` (optional) names the target media Collection — ADR 0009 spells
136
+ * `MediaRef("images")`. v1 keeps the projection deliberately SANE and
137
+ * scope-bounded: the value projects as a nested object (D1 TEXT/JSON,
138
+ * inline TS type, Puck object, OpenAPI object) and does NOT break any of the
139
+ * four walkers. FK-column compilation + relation resolution are explicitly
140
+ * deferred.
141
+ */
142
+ export declare function MediaRef(slug?: string): Schema.Schema<ResolvedMediaRef, ResolvedMediaRef>;
143
+ export interface TransformParams {
144
+ readonly width?: number;
145
+ readonly height?: number;
146
+ readonly format?: "webp" | "avif" | "jpeg" | "png";
147
+ readonly quality?: number;
148
+ }
149
+ /**
150
+ * Build a Cloudflare Image Transformations URL from a base object URL.
151
+ *
152
+ * Cloudflare's documented URL structure:
153
+ * https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>
154
+ *
155
+ * With no transform (or all-empty) the base URL is returned UNCHANGED.
156
+ *
157
+ * For bucket-mode records only — do NOT call with `publicUrl` from a
158
+ * repo-mode record. Repo-mode `.url()` returns `publicUrl` directly.
159
+ */
160
+ export declare function mediaTransformUrl(baseUrl: string, transform?: TransformParams): string;
161
+ /**
162
+ * The mode-agnostic accessor a Block/Page renderer holds (ADR 0009 §6).
163
+ *
164
+ * `.url(transform?)`:
165
+ * - bucket mode: returns `mediaTransformUrl(originalUrl, transform)`.
166
+ * - repo mode: returns `publicUrl` UNCHANGED regardless of `transform` —
167
+ * repo-mode transforms are the host framework's build-time job (`<Image>`),
168
+ * not a CF on-demand URL. Passing a transform to a repo-mode accessor is
169
+ * a no-op by contract (document in consuming code if needed).
170
+ *
171
+ * `.srcset(...)` is the documented v1.x deferred seam — throws `NOT_IMPLEMENTED`
172
+ * for both modes.
173
+ */
174
+ export interface MediaRefAccessor {
175
+ readonly id: string;
176
+ readonly mime: string;
177
+ readonly width: number;
178
+ readonly height: number;
179
+ url(transform?: TransformParams): string;
180
+ srcset(widths: ReadonlyArray<number>, format?: "webp" | "avif"): never;
181
+ }
182
+ /**
183
+ * Wrap a resolved media record in a `MediaRefAccessor`. Mode is detected
184
+ * structurally: presence of `publicUrl` signals repo mode; absence signals
185
+ * bucket mode.
186
+ */
187
+ export declare function resolveMediaRef(record: ResolvedMediaRef): MediaRefAccessor;
188
+ /**
189
+ * Derive the committed filename for a repo-mode media upload.
190
+ *
191
+ * Policies (ADR 0009 "Naming policy", CONTEXT.md "Naming policy"):
192
+ * - `"content-hash"` (default): `<sha256hex-16>.<ext>` — first 16 hex chars
193
+ * of the SHA-256 digest of the raw bytes. Collision-safe and cache-safe:
194
+ * same bytes → same name; any byte change → different name.
195
+ * - `"slug"`: filesystem-safe slug of `originalFilename` (lowercase, `[a-z0-9-]`,
196
+ * runs collapsed, leading/trailing hyphens stripped) + `.<ext>`. Falls back
197
+ * to content-hash when `originalFilename` is absent or produces an empty slug
198
+ * after sanitization (e.g. all non-ASCII input).
199
+ *
200
+ * Async only for Web Crypto (`crypto.subtle.digest`). No I/O, no network.
201
+ */
202
+ export declare function mediaRepoName(opts: {
203
+ readonly bytes: Uint8Array | ArrayBuffer;
204
+ readonly originalFilename?: string;
205
+ readonly ext: string;
206
+ readonly policy?: "content-hash" | "slug";
207
+ }): Promise<string>;
208
+ /**
209
+ * Compute the committed path and served URL for a repo-mode media upload.
210
+ *
211
+ * Returns a plan `{ repoPath, publicUrl, bytes }` that the Publish pipeline
212
+ * (via the GitHub App) will execute. This function itself does NOT write or
213
+ * commit anything — it is pure aside from the Web Crypto digest.
214
+ *
215
+ * Path derivation (ADR 0009:105):
216
+ * `repoPath = <normalizedAssetRoot>saacms/<collectionSlug>/<name>`
217
+ * `publicUrl = /saacms/<collectionSlug>/<name>`
218
+ *
219
+ * where `<name>` = `mediaRepoName({ bytes, originalFilename, ext, policy })`.
220
+ *
221
+ * Path safety: `collectionSlug` and `originalFilename` (if provided) are
222
+ * checked for `..`, absolute paths, and backslashes before use. Any violation
223
+ * throws a precise `Error`. Same discipline as host-astro's `assertSafeUrl`.
224
+ */
225
+ export declare function stageRepoMedia(opts: {
226
+ readonly collectionSlug: string;
227
+ readonly assetRoot: string;
228
+ readonly bytes: Uint8Array | ArrayBuffer;
229
+ readonly originalFilename?: string;
230
+ readonly mime: string;
231
+ readonly policy?: "content-hash" | "slug";
232
+ }): Promise<{
233
+ repoPath: string;
234
+ publicUrl: string;
235
+ bytes: Uint8Array;
236
+ }>;
237
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/schema/media.ts"],"names":[],"mappings":"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"}