@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,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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"}
|