@caretcms/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/LICENSE +21 -0
- package/README.md +191 -0
- package/dist/browser-runtime.d.ts +12 -0
- package/dist/browser-runtime.d.ts.map +1 -0
- package/dist/browser-runtime.js +298 -0
- package/dist/browser-runtime.js.map +1 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +343 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +61 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +80 -0
- package/dist/loader.js.map +1 -0
- package/dist/providers/storage/filesystem.d.ts +7 -0
- package/dist/providers/storage/filesystem.d.ts.map +1 -0
- package/dist/providers/storage/filesystem.js +6 -0
- package/dist/providers/storage/filesystem.js.map +1 -0
- package/dist/providers/uploads/local.d.ts +6 -0
- package/dist/providers/uploads/local.d.ts.map +1 -0
- package/dist/providers/uploads/local.js +6 -0
- package/dist/providers/uploads/local.js.map +1 -0
- package/dist/runtime/auth/cookie-utils.d.ts +3 -0
- package/dist/runtime/auth/cookie-utils.d.ts.map +1 -0
- package/dist/runtime/auth/cookie-utils.js +33 -0
- package/dist/runtime/auth/cookie-utils.js.map +1 -0
- package/dist/runtime/auth/demo-session.d.ts +24 -0
- package/dist/runtime/auth/demo-session.d.ts.map +1 -0
- package/dist/runtime/auth/demo-session.js +42 -0
- package/dist/runtime/auth/demo-session.js.map +1 -0
- package/dist/runtime/auth/rate-limiter.d.ts +26 -0
- package/dist/runtime/auth/rate-limiter.d.ts.map +1 -0
- package/dist/runtime/auth/rate-limiter.js +65 -0
- package/dist/runtime/auth/rate-limiter.js.map +1 -0
- package/dist/runtime/auth/session.d.ts +14 -0
- package/dist/runtime/auth/session.d.ts.map +1 -0
- package/dist/runtime/auth/session.js +128 -0
- package/dist/runtime/auth/session.js.map +1 -0
- package/dist/runtime/bind.d.ts +14 -0
- package/dist/runtime/bind.d.ts.map +1 -0
- package/dist/runtime/bind.js +19 -0
- package/dist/runtime/bind.js.map +1 -0
- package/dist/runtime/config.d.ts +20 -0
- package/dist/runtime/config.d.ts.map +1 -0
- package/dist/runtime/config.js +40 -0
- package/dist/runtime/config.js.map +1 -0
- package/dist/runtime/content.d.ts +28 -0
- package/dist/runtime/content.d.ts.map +1 -0
- package/dist/runtime/content.js +37 -0
- package/dist/runtime/content.js.map +1 -0
- package/dist/runtime/index.d.ts +14 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +16 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/middleware.d.ts +14 -0
- package/dist/runtime/middleware.d.ts.map +1 -0
- package/dist/runtime/middleware.js +80 -0
- package/dist/runtime/middleware.js.map +1 -0
- package/dist/runtime/mutations/contracts.d.ts +91 -0
- package/dist/runtime/mutations/contracts.d.ts.map +1 -0
- package/dist/runtime/mutations/contracts.js +320 -0
- package/dist/runtime/mutations/contracts.js.map +1 -0
- package/dist/runtime/mutations/engine.d.ts +23 -0
- package/dist/runtime/mutations/engine.d.ts.map +1 -0
- package/dist/runtime/mutations/engine.js +255 -0
- package/dist/runtime/mutations/engine.js.map +1 -0
- package/dist/runtime/providers.d.ts +9 -0
- package/dist/runtime/providers.d.ts.map +1 -0
- package/dist/runtime/providers.js +44 -0
- package/dist/runtime/providers.js.map +1 -0
- package/dist/runtime/request-context.d.ts +11 -0
- package/dist/runtime/request-context.d.ts.map +1 -0
- package/dist/runtime/request-context.js +16 -0
- package/dist/runtime/request-context.js.map +1 -0
- package/dist/runtime/rewrite.d.ts +13 -0
- package/dist/runtime/rewrite.d.ts.map +1 -0
- package/dist/runtime/rewrite.js +253 -0
- package/dist/runtime/rewrite.js.map +1 -0
- package/dist/runtime/routes/_helpers.d.ts +17 -0
- package/dist/runtime/routes/_helpers.d.ts.map +1 -0
- package/dist/runtime/routes/_helpers.js +38 -0
- package/dist/runtime/routes/_helpers.js.map +1 -0
- package/dist/runtime/routes/admin-entry.d.ts +3 -0
- package/dist/runtime/routes/admin-entry.d.ts.map +1 -0
- package/dist/runtime/routes/admin-entry.js +170 -0
- package/dist/runtime/routes/admin-entry.js.map +1 -0
- package/dist/runtime/routes/admin.d.ts +3 -0
- package/dist/runtime/routes/admin.d.ts.map +1 -0
- package/dist/runtime/routes/admin.js +189 -0
- package/dist/runtime/routes/admin.js.map +1 -0
- package/dist/runtime/routes/auth-login.d.ts +4 -0
- package/dist/runtime/routes/auth-login.d.ts.map +1 -0
- package/dist/runtime/routes/auth-login.js +71 -0
- package/dist/runtime/routes/auth-login.js.map +1 -0
- package/dist/runtime/routes/auth-logout.d.ts +4 -0
- package/dist/runtime/routes/auth-logout.d.ts.map +1 -0
- package/dist/runtime/routes/auth-logout.js +47 -0
- package/dist/runtime/routes/auth-logout.js.map +1 -0
- package/dist/runtime/routes/auth-session.d.ts +3 -0
- package/dist/runtime/routes/auth-session.d.ts.map +1 -0
- package/dist/runtime/routes/auth-session.js +8 -0
- package/dist/runtime/routes/auth-session.js.map +1 -0
- package/dist/runtime/routes/collection-list.d.ts +3 -0
- package/dist/runtime/routes/collection-list.d.ts.map +1 -0
- package/dist/runtime/routes/collection-list.js +420 -0
- package/dist/runtime/routes/collection-list.js.map +1 -0
- package/dist/runtime/routes/collections-metadata.d.ts +3 -0
- package/dist/runtime/routes/collections-metadata.d.ts.map +1 -0
- package/dist/runtime/routes/collections-metadata.js +17 -0
- package/dist/runtime/routes/collections-metadata.js.map +1 -0
- package/dist/runtime/routes/editor-assets.d.ts +3 -0
- package/dist/runtime/routes/editor-assets.d.ts.map +1 -0
- package/dist/runtime/routes/editor-assets.js +73 -0
- package/dist/runtime/routes/editor-assets.js.map +1 -0
- package/dist/runtime/routes/entries.d.ts +3 -0
- package/dist/runtime/routes/entries.d.ts.map +1 -0
- package/dist/runtime/routes/entries.js +96 -0
- package/dist/runtime/routes/entries.js.map +1 -0
- package/dist/runtime/routes/history.d.ts +4 -0
- package/dist/runtime/routes/history.d.ts.map +1 -0
- package/dist/runtime/routes/history.js +54 -0
- package/dist/runtime/routes/history.js.map +1 -0
- package/dist/runtime/routes/mutate.d.ts +4 -0
- package/dist/runtime/routes/mutate.d.ts.map +1 -0
- package/dist/runtime/routes/mutate.js +38 -0
- package/dist/runtime/routes/mutate.js.map +1 -0
- package/dist/runtime/routes/schema.d.ts +3 -0
- package/dist/runtime/routes/schema.d.ts.map +1 -0
- package/dist/runtime/routes/schema.js +58 -0
- package/dist/runtime/routes/schema.js.map +1 -0
- package/dist/runtime/routes/studio-home.d.ts +3 -0
- package/dist/runtime/routes/studio-home.d.ts.map +1 -0
- package/dist/runtime/routes/studio-home.js +99 -0
- package/dist/runtime/routes/studio-home.js.map +1 -0
- package/dist/runtime/routes/theme-stylesheet.d.ts +3 -0
- package/dist/runtime/routes/theme-stylesheet.d.ts.map +1 -0
- package/dist/runtime/routes/theme-stylesheet.js +32 -0
- package/dist/runtime/routes/theme-stylesheet.js.map +1 -0
- package/dist/runtime/routes/upload.d.ts +3 -0
- package/dist/runtime/routes/upload.d.ts.map +1 -0
- package/dist/runtime/routes/upload.js +33 -0
- package/dist/runtime/routes/upload.js.map +1 -0
- package/dist/runtime/sanitize-html.d.ts +8 -0
- package/dist/runtime/sanitize-html.d.ts.map +1 -0
- package/dist/runtime/sanitize-html.js +86 -0
- package/dist/runtime/sanitize-html.js.map +1 -0
- package/dist/runtime/schema/template.d.ts +15 -0
- package/dist/runtime/schema/template.d.ts.map +1 -0
- package/dist/runtime/schema/template.js +38 -0
- package/dist/runtime/schema/template.js.map +1 -0
- package/dist/runtime/schema-registry.d.ts +17 -0
- package/dist/runtime/schema-registry.d.ts.map +1 -0
- package/dist/runtime/schema-registry.js +16 -0
- package/dist/runtime/schema-registry.js.map +1 -0
- package/dist/runtime/storage/filesystem-adapter.d.ts +36 -0
- package/dist/runtime/storage/filesystem-adapter.d.ts.map +1 -0
- package/dist/runtime/storage/filesystem-adapter.js +291 -0
- package/dist/runtime/storage/filesystem-adapter.js.map +1 -0
- package/dist/runtime/storage/filesystem-upload-handler.d.ts +11 -0
- package/dist/runtime/storage/filesystem-upload-handler.d.ts.map +1 -0
- package/dist/runtime/storage/filesystem-upload-handler.js +20 -0
- package/dist/runtime/storage/filesystem-upload-handler.js.map +1 -0
- package/dist/runtime/storage/image-validation.d.ts +28 -0
- package/dist/runtime/storage/image-validation.d.ts.map +1 -0
- package/dist/runtime/storage/image-validation.js +94 -0
- package/dist/runtime/storage/image-validation.js.map +1 -0
- package/dist/runtime/storage/in-memory-adapter.d.ts +29 -0
- package/dist/runtime/storage/in-memory-adapter.d.ts.map +1 -0
- package/dist/runtime/storage/in-memory-adapter.js +110 -0
- package/dist/runtime/storage/in-memory-adapter.js.map +1 -0
- package/dist/runtime/storage/quota-upload-handler.d.ts +27 -0
- package/dist/runtime/storage/quota-upload-handler.d.ts.map +1 -0
- package/dist/runtime/storage/quota-upload-handler.js +34 -0
- package/dist/runtime/storage/quota-upload-handler.js.map +1 -0
- package/dist/runtime/storage/session-overlay-adapter.d.ts +33 -0
- package/dist/runtime/storage/session-overlay-adapter.d.ts.map +1 -0
- package/dist/runtime/storage/session-overlay-adapter.js +121 -0
- package/dist/runtime/storage/session-overlay-adapter.js.map +1 -0
- package/dist/runtime/themes/preset-tokens.d.ts +17 -0
- package/dist/runtime/themes/preset-tokens.d.ts.map +1 -0
- package/dist/runtime/themes/preset-tokens.js +47 -0
- package/dist/runtime/themes/preset-tokens.js.map +1 -0
- package/dist/runtime/utils.d.ts +6 -0
- package/dist/runtime/utils.d.ts.map +1 -0
- package/dist/runtime/utils.js +29 -0
- package/dist/runtime/utils.js.map +1 -0
- package/dist/runtime/views/studio-layout.d.ts +25 -0
- package/dist/runtime/views/studio-layout.d.ts.map +1 -0
- package/dist/runtime/views/studio-layout.js +108 -0
- package/dist/runtime/views/studio-layout.js.map +1 -0
- package/dist/schema-utils.d.ts +16 -0
- package/dist/schema-utils.d.ts.map +1 -0
- package/dist/schema-utils.js +62 -0
- package/dist/schema-utils.js.map +1 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/editor-runtime.d.ts +6 -0
- package/editor-runtime.js +2 -0
- package/package.json +88 -0
- package/static/cms/admin-entry.css +207 -0
- package/static/cms/admin-entry.js +1083 -0
- package/static/cms/editor/config.js +118 -0
- package/static/cms/editor/content-map.css +141 -0
- package/static/cms/editor/content-map.js +172 -0
- package/static/cms/editor/guards.js +72 -0
- package/static/cms/editor/helpers.js +51 -0
- package/static/cms/editor/image-edit.js +151 -0
- package/static/cms/editor/image-utils.js +84 -0
- package/static/cms/editor/image.css +133 -0
- package/static/cms/editor/link-follow.js +144 -0
- package/static/cms/editor/link-ui.css +199 -0
- package/static/cms/editor/linkify.js +55 -0
- package/static/cms/editor/panel.css +91 -0
- package/static/cms/editor/panel.js +70 -0
- package/static/cms/editor/rich-toolbar.css +67 -0
- package/static/cms/editor/rich-toolbar.js +200 -0
- package/static/cms/editor/sanitize.js +89 -0
- package/static/cms/editor/save-queue.js +166 -0
- package/static/cms/editor/section-controls/activation.js +10 -0
- package/static/cms/editor/section-controls/api.js +87 -0
- package/static/cms/editor/section-controls/constants.js +24 -0
- package/static/cms/editor/section-controls/index.js +622 -0
- package/static/cms/editor/section-controls/model.js +76 -0
- package/static/cms/editor/section-controls/mutations.js +112 -0
- package/static/cms/editor/section-controls/page-context.js +34 -0
- package/static/cms/editor/section-controls/pickers.js +196 -0
- package/static/cms/editor/section-controls/reorder.js +92 -0
- package/static/cms/editor/section-controls/selection.js +54 -0
- package/static/cms/editor/section-controls/spacing-drag.js +83 -0
- package/static/cms/editor/section-controls/ui-elements.js +54 -0
- package/static/cms/editor/section-controls/utils.js +35 -0
- package/static/cms/editor/section-controls.css +349 -0
- package/static/cms/editor/section-controls.js +1 -0
- package/static/cms/editor/security.js +48 -0
- package/static/cms/editor/sync.js +72 -0
- package/static/cms/editor/text-edit.js +151 -0
- package/static/cms/editor/text-link-interactions.js +254 -0
- package/static/cms/editor/toast.css +50 -0
- package/static/cms/editor/toast.js +29 -0
- package/static/cms/editor/tokens-and-text.css +93 -0
- package/static/cms/editor/toolbar.css +249 -0
- package/static/cms/editor/toolbar.js +117 -0
- package/static/cms/editor.css +12 -0
- package/static/cms/editor.js +116 -0
- package/static/cms/studio-login.css +300 -0
- package/static/cms/studio-tokens.css +85 -0
- package/static/cms/studio.css +437 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a default entry template from a JSON Schema.
|
|
3
|
+
*
|
|
4
|
+
* Used to seed initial data for new entries in dynamically-created
|
|
5
|
+
* collections. Pure JSON-Schema → value mapping; no validation library
|
|
6
|
+
* (schemas reach core as JSON Schema, e.g. via the caller's own
|
|
7
|
+
* `z.toJSONSchema()`), so core stays Zod-agnostic.
|
|
8
|
+
*/
|
|
9
|
+
import type { CollectionSchema } from "../../types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Generate a default template from a JSON Schema.
|
|
12
|
+
* Used to create initial data for new entries in dynamic collections.
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateTemplateFromSchema(jsonSchema: CollectionSchema): Record<string, unknown>;
|
|
15
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../src/runtime/schema/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAOvD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBhG"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a default entry template from a JSON Schema.
|
|
3
|
+
*
|
|
4
|
+
* Used to seed initial data for new entries in dynamically-created
|
|
5
|
+
* collections. Pure JSON-Schema → value mapping; no validation library
|
|
6
|
+
* (schemas reach core as JSON Schema, e.g. via the caller's own
|
|
7
|
+
* `z.toJSONSchema()`), so core stays Zod-agnostic.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Generate a default template from a JSON Schema.
|
|
11
|
+
* Used to create initial data for new entries in dynamic collections.
|
|
12
|
+
*/
|
|
13
|
+
export function generateTemplateFromSchema(jsonSchema) {
|
|
14
|
+
const template = {};
|
|
15
|
+
for (const [key, prop] of Object.entries(jsonSchema.properties)) {
|
|
16
|
+
const p = prop;
|
|
17
|
+
if (p.default !== undefined) {
|
|
18
|
+
template[key] = p.default;
|
|
19
|
+
}
|
|
20
|
+
else if (p.type === "string") {
|
|
21
|
+
template[key] = "";
|
|
22
|
+
}
|
|
23
|
+
else if (p.type === "number" || p.type === "integer") {
|
|
24
|
+
template[key] = 0;
|
|
25
|
+
}
|
|
26
|
+
else if (p.type === "boolean") {
|
|
27
|
+
template[key] = false;
|
|
28
|
+
}
|
|
29
|
+
else if (p.type === "array") {
|
|
30
|
+
template[key] = [];
|
|
31
|
+
}
|
|
32
|
+
else if (p.type === "object") {
|
|
33
|
+
template[key] = {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return template;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/runtime/schema/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAA4B;IACrE,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAChE,MAAM,CAAC,GAAG,IAA0B,CAAC;QAErC,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC5B,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;QAC5B,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC/B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACvD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAChC,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACxB,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC9B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC/B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema registry for explicit Zod-derived JSON Schemas.
|
|
3
|
+
*
|
|
4
|
+
* When the user passes `schemas` in caret() options, the integration
|
|
5
|
+
* serializes them and injects them into a virtual module. The schema API
|
|
6
|
+
* route reads from this registry and falls back to inference only when
|
|
7
|
+
* no explicit schema is registered for a collection.
|
|
8
|
+
*/
|
|
9
|
+
import type { JsonSchemaNode } from "../schema-utils.js";
|
|
10
|
+
type CollectionSchemaEntry = {
|
|
11
|
+
schema: JsonSchemaNode;
|
|
12
|
+
template: Record<string, unknown> | null;
|
|
13
|
+
};
|
|
14
|
+
export declare function registerCollectionSchema(collection: string, jsonSchema: JsonSchemaNode, template: Record<string, unknown> | null): void;
|
|
15
|
+
export declare function getRegisteredSchema(collection: string): CollectionSchemaEntry | null;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=schema-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/schema-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,KAAK,qBAAqB,GAAG;IAC3B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1C,CAAC;AAIF,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACvC,IAAI,CAEN;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,GACjB,qBAAqB,GAAG,IAAI,CAE9B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema registry for explicit Zod-derived JSON Schemas.
|
|
3
|
+
*
|
|
4
|
+
* When the user passes `schemas` in caret() options, the integration
|
|
5
|
+
* serializes them and injects them into a virtual module. The schema API
|
|
6
|
+
* route reads from this registry and falls back to inference only when
|
|
7
|
+
* no explicit schema is registered for a collection.
|
|
8
|
+
*/
|
|
9
|
+
const registry = new Map();
|
|
10
|
+
export function registerCollectionSchema(collection, jsonSchema, template) {
|
|
11
|
+
registry.set(collection, { schema: jsonSchema, template });
|
|
12
|
+
}
|
|
13
|
+
export function getRegisteredSchema(collection) {
|
|
14
|
+
return registry.get(collection) ?? null;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=schema-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-registry.js","sourceRoot":"","sources":["../../src/runtime/schema-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAiC,CAAC;AAE1D,MAAM,UAAU,wBAAwB,CACtC,UAAkB,EAClB,UAA0B,EAC1B,QAAwC;IAExC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,UAAkB;IAElB,OAAO,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CollectionMetadata, EntryData, HistoryEntry, StorageAdapter } from "../../types.js";
|
|
2
|
+
export declare class FilesystemAdapter implements StorageAdapter {
|
|
3
|
+
private readonly dataRoot;
|
|
4
|
+
private readonly metaRoot;
|
|
5
|
+
constructor(options?: {
|
|
6
|
+
dataRoot?: string;
|
|
7
|
+
metaRoot?: string;
|
|
8
|
+
});
|
|
9
|
+
private collectionDir;
|
|
10
|
+
private revisionStorePath;
|
|
11
|
+
private revisionMapKey;
|
|
12
|
+
private historyFilePath;
|
|
13
|
+
private discoverFromDir;
|
|
14
|
+
discoverCollections(): Promise<string[]>;
|
|
15
|
+
isKnownCollection(collection: string): Promise<boolean>;
|
|
16
|
+
getEntry(collection: string, id: string): Promise<EntryData | null>;
|
|
17
|
+
listEntryIds(collection: string): Promise<string[]>;
|
|
18
|
+
listEntries(collection: string): Promise<EntryData[]>;
|
|
19
|
+
writeEntry(collection: string, id: string, data: Record<string, unknown>): Promise<void>;
|
|
20
|
+
deleteEntry(collection: string, id: string): Promise<void>;
|
|
21
|
+
private readRevisionMap;
|
|
22
|
+
private writeRevisionMap;
|
|
23
|
+
getRevision(collection: string, id: string): Promise<number>;
|
|
24
|
+
bumpRevision(collection: string, id: string): Promise<number>;
|
|
25
|
+
private readHistoryFile;
|
|
26
|
+
private writeHistoryFile;
|
|
27
|
+
getHistory(collection: string, id: string): Promise<HistoryEntry[]>;
|
|
28
|
+
appendHistory(collection: string, id: string, entry: HistoryEntry): Promise<void>;
|
|
29
|
+
private collectionsMetaDir;
|
|
30
|
+
private collectionMetaPath;
|
|
31
|
+
createCollection(metadata: CollectionMetadata): Promise<void>;
|
|
32
|
+
deleteCollection(collection: string): Promise<void>;
|
|
33
|
+
getCollectionMetadata(collection: string): Promise<CollectionMetadata | null>;
|
|
34
|
+
listCollectionMetadata(): Promise<CollectionMetadata[]>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=filesystem-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-adapter.d.ts","sourceRoot":"","sources":["../../../src/runtime/storage/filesystem-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAiFlG,qBAAa,iBAAkB,YAAW,cAAc;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;IAM9D,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,eAAe;YAMT,eAAe;IAWvB,mBAAmB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IASxC,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQvD,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAanE,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAYnD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMrD,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAalD,eAAe;YAmBf,gBAAgB;IAMxB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM5D,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAcrD,eAAe;YAYf,gBAAgB;IAUxB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAInE,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAQvF,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,kBAAkB;IAIpB,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7D,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAenD,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAY7E,sBAAsB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;CAiB9D"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, rm, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const COLLECTION_NAME_RE = /^[a-z][a-z0-9_-]*$/;
|
|
4
|
+
const HISTORY_LIMIT = 50;
|
|
5
|
+
/** workerd (incl. Astro 6's dev server) sets navigator.userAgent to this. */
|
|
6
|
+
const WORKERD_USER_AGENT = "Cloudflare-Workers";
|
|
7
|
+
/**
|
|
8
|
+
* Return a human-readable runtime name if filesystem storage cannot work here,
|
|
9
|
+
* or null when a real Node filesystem is available.
|
|
10
|
+
*
|
|
11
|
+
* Astro 6's dev server runs on the same workerd runtime as production, so a
|
|
12
|
+
* Workers-targeted project has no filesystem in dev *or* prod — the failure
|
|
13
|
+
* would otherwise surface as silently-empty reads and cryptic write errors.
|
|
14
|
+
*/
|
|
15
|
+
function detectUnsupportedRuntime() {
|
|
16
|
+
const g = globalThis;
|
|
17
|
+
// navigator.userAgent === "Cloudflare-Workers" only when the `global_navigator`
|
|
18
|
+
// compat flag is on, so also check WebSocketPair — a workerd global present
|
|
19
|
+
// regardless of compat flags — to detect Workers even on an old compat date.
|
|
20
|
+
if (g.navigator?.userAgent === WORKERD_USER_AGENT || typeof g.WebSocketPair === "function") {
|
|
21
|
+
return "Cloudflare Workers (workerd)";
|
|
22
|
+
}
|
|
23
|
+
if (typeof process === "undefined" || typeof process.cwd !== "function") {
|
|
24
|
+
return "a non-Node runtime";
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function assertFilesystemRuntime() {
|
|
29
|
+
const runtime = detectUnsupportedRuntime();
|
|
30
|
+
if (!runtime)
|
|
31
|
+
return;
|
|
32
|
+
throw new Error(`[caretcms] Filesystem storage is not available on ${runtime}.\n` +
|
|
33
|
+
`Astro 6's dev server also runs on workerd, so this fails in dev as well as production.\n\n` +
|
|
34
|
+
`Targeting Cloudflare Workers? Use the KV adapter from @caretcms/cloudflare in BOTH dev and prod:\n\n` +
|
|
35
|
+
` import { cloudflareStorage } from '@caretcms/cloudflare';\n` +
|
|
36
|
+
` // caret({ storage: cloudflareStorage(), ... })\n\n` +
|
|
37
|
+
`Targeting a Node host? Filesystem storage works there — make sure you are not building for an edge runtime.`);
|
|
38
|
+
}
|
|
39
|
+
function asObjectRecord(value) {
|
|
40
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
41
|
+
return null;
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function isHistoryEntry(value) {
|
|
45
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
46
|
+
return false;
|
|
47
|
+
const obj = value;
|
|
48
|
+
return (typeof obj.ts === "number" &&
|
|
49
|
+
Number.isFinite(obj.ts) &&
|
|
50
|
+
typeof obj.action === "string" &&
|
|
51
|
+
"data" in obj);
|
|
52
|
+
}
|
|
53
|
+
function isValidRevision(value) {
|
|
54
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
55
|
+
}
|
|
56
|
+
const revisionWriteChains = new Map();
|
|
57
|
+
function serializeRevisionWrite(filePath, task) {
|
|
58
|
+
const previous = revisionWriteChains.get(filePath) ?? Promise.resolve();
|
|
59
|
+
const next = previous.then(task, task);
|
|
60
|
+
revisionWriteChains.set(filePath, next.finally(() => {
|
|
61
|
+
if (revisionWriteChains.get(filePath) === next) {
|
|
62
|
+
revisionWriteChains.delete(filePath);
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
export class FilesystemAdapter {
|
|
68
|
+
dataRoot;
|
|
69
|
+
metaRoot;
|
|
70
|
+
constructor(options) {
|
|
71
|
+
assertFilesystemRuntime();
|
|
72
|
+
this.dataRoot = options?.dataRoot ?? join(process.cwd(), ".caret", "data");
|
|
73
|
+
this.metaRoot = options?.metaRoot ?? join(process.cwd(), ".caretcms");
|
|
74
|
+
}
|
|
75
|
+
collectionDir(collection) {
|
|
76
|
+
return join(this.dataRoot, collection);
|
|
77
|
+
}
|
|
78
|
+
revisionStorePath() {
|
|
79
|
+
return join(this.metaRoot, "revisions.json");
|
|
80
|
+
}
|
|
81
|
+
revisionMapKey(collection, id) {
|
|
82
|
+
return `${collection}::${id}`;
|
|
83
|
+
}
|
|
84
|
+
historyFilePath(collection, id) {
|
|
85
|
+
return join(this.metaRoot, "history", collection, `${id}.json`);
|
|
86
|
+
}
|
|
87
|
+
// --- Collections ---
|
|
88
|
+
async discoverFromDir(dir) {
|
|
89
|
+
try {
|
|
90
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
91
|
+
return entries
|
|
92
|
+
.filter((e) => e.isDirectory() && COLLECTION_NAME_RE.test(e.name))
|
|
93
|
+
.map((e) => e.name);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async discoverCollections() {
|
|
100
|
+
const [fromData, fromMeta] = await Promise.all([
|
|
101
|
+
this.discoverFromDir(this.dataRoot),
|
|
102
|
+
this.discoverFromDir(join(this.metaRoot, "history")),
|
|
103
|
+
]);
|
|
104
|
+
const merged = new Set([...fromData, ...fromMeta]);
|
|
105
|
+
return [...merged].sort((a, b) => a.localeCompare(b));
|
|
106
|
+
}
|
|
107
|
+
async isKnownCollection(collection) {
|
|
108
|
+
if (!COLLECTION_NAME_RE.test(collection))
|
|
109
|
+
return false;
|
|
110
|
+
const collections = await this.discoverCollections();
|
|
111
|
+
return collections.includes(collection);
|
|
112
|
+
}
|
|
113
|
+
// --- Entries ---
|
|
114
|
+
async getEntry(collection, id) {
|
|
115
|
+
const filePath = join(this.collectionDir(collection), `${id}.json`);
|
|
116
|
+
try {
|
|
117
|
+
const raw = await readFile(filePath, "utf8");
|
|
118
|
+
const parsed = JSON.parse(raw);
|
|
119
|
+
const data = asObjectRecord(parsed);
|
|
120
|
+
if (!data)
|
|
121
|
+
return null;
|
|
122
|
+
return { id, data };
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async listEntryIds(collection) {
|
|
129
|
+
try {
|
|
130
|
+
const files = await readdir(this.collectionDir(collection), { withFileTypes: true });
|
|
131
|
+
return files
|
|
132
|
+
.filter((item) => item.isFile() && item.name.endsWith(".json"))
|
|
133
|
+
.map((item) => item.name.slice(0, -".json".length))
|
|
134
|
+
.sort((a, b) => a.localeCompare(b));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async listEntries(collection) {
|
|
141
|
+
const ids = await this.listEntryIds(collection);
|
|
142
|
+
const entries = await Promise.all(ids.map((id) => this.getEntry(collection, id)));
|
|
143
|
+
return entries.filter((entry) => Boolean(entry));
|
|
144
|
+
}
|
|
145
|
+
async writeEntry(collection, id, data) {
|
|
146
|
+
const dir = this.collectionDir(collection);
|
|
147
|
+
await mkdir(dir, { recursive: true });
|
|
148
|
+
const filePath = join(dir, `${id}.json`);
|
|
149
|
+
await writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
150
|
+
}
|
|
151
|
+
async deleteEntry(collection, id) {
|
|
152
|
+
const filePath = join(this.collectionDir(collection), `${id}.json`);
|
|
153
|
+
try {
|
|
154
|
+
await unlink(filePath);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const maybeErr = error;
|
|
158
|
+
if (maybeErr?.code === "ENOENT")
|
|
159
|
+
return;
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// --- Revisions ---
|
|
164
|
+
async readRevisionMap() {
|
|
165
|
+
const filePath = this.revisionStorePath();
|
|
166
|
+
try {
|
|
167
|
+
const raw = await readFile(filePath, "utf8");
|
|
168
|
+
const parsed = JSON.parse(raw);
|
|
169
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
170
|
+
return {};
|
|
171
|
+
const map = parsed;
|
|
172
|
+
const out = {};
|
|
173
|
+
Object.entries(map).forEach(([key, value]) => {
|
|
174
|
+
if (isValidRevision(value)) {
|
|
175
|
+
out[key] = value;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return {};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async writeRevisionMap(map) {
|
|
185
|
+
const filePath = this.revisionStorePath();
|
|
186
|
+
await mkdir(this.metaRoot, { recursive: true });
|
|
187
|
+
await writeFile(filePath, `${JSON.stringify(map, null, 2)}\n`, "utf8");
|
|
188
|
+
}
|
|
189
|
+
async getRevision(collection, id) {
|
|
190
|
+
const map = await this.readRevisionMap();
|
|
191
|
+
const value = map[this.revisionMapKey(collection, id)];
|
|
192
|
+
return isValidRevision(value) ? value : 0;
|
|
193
|
+
}
|
|
194
|
+
async bumpRevision(collection, id) {
|
|
195
|
+
return serializeRevisionWrite(this.revisionStorePath(), async () => {
|
|
196
|
+
const map = await this.readRevisionMap();
|
|
197
|
+
const key = this.revisionMapKey(collection, id);
|
|
198
|
+
const current = isValidRevision(map[key]) ? map[key] : 0;
|
|
199
|
+
const next = current + 1;
|
|
200
|
+
map[key] = next;
|
|
201
|
+
await this.writeRevisionMap(map);
|
|
202
|
+
return next;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
// --- History ---
|
|
206
|
+
async readHistoryFile(collection, id) {
|
|
207
|
+
const filePath = this.historyFilePath(collection, id);
|
|
208
|
+
try {
|
|
209
|
+
const raw = await readFile(filePath, "utf8");
|
|
210
|
+
const parsed = JSON.parse(raw);
|
|
211
|
+
if (!Array.isArray(parsed))
|
|
212
|
+
return [];
|
|
213
|
+
return parsed.filter(isHistoryEntry);
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async writeHistoryFile(collection, id, entries) {
|
|
220
|
+
const filePath = this.historyFilePath(collection, id);
|
|
221
|
+
await mkdir(join(this.metaRoot, "history", collection), { recursive: true });
|
|
222
|
+
await writeFile(filePath, `${JSON.stringify(entries, null, 2)}\n`, "utf8");
|
|
223
|
+
}
|
|
224
|
+
async getHistory(collection, id) {
|
|
225
|
+
return this.readHistoryFile(collection, id);
|
|
226
|
+
}
|
|
227
|
+
async appendHistory(collection, id, entry) {
|
|
228
|
+
const current = await this.readHistoryFile(collection, id);
|
|
229
|
+
const next = [entry, ...current].slice(0, HISTORY_LIMIT);
|
|
230
|
+
await this.writeHistoryFile(collection, id, next);
|
|
231
|
+
}
|
|
232
|
+
// --- Collection Management ---
|
|
233
|
+
collectionsMetaDir() {
|
|
234
|
+
return join(this.metaRoot, "collections");
|
|
235
|
+
}
|
|
236
|
+
collectionMetaPath(collection) {
|
|
237
|
+
return join(this.collectionsMetaDir(), `${collection}.json`);
|
|
238
|
+
}
|
|
239
|
+
async createCollection(metadata) {
|
|
240
|
+
const dir = this.collectionsMetaDir();
|
|
241
|
+
await mkdir(dir, { recursive: true });
|
|
242
|
+
const filePath = this.collectionMetaPath(metadata.id);
|
|
243
|
+
await writeFile(filePath, `${JSON.stringify(metadata, null, 2)}\n`, "utf8");
|
|
244
|
+
// Create empty collection directory
|
|
245
|
+
const collectionDir = this.collectionDir(metadata.id);
|
|
246
|
+
await mkdir(collectionDir, { recursive: true });
|
|
247
|
+
}
|
|
248
|
+
async deleteCollection(collection) {
|
|
249
|
+
// unlink has no `force` option, so guard the metadata file against ENOENT.
|
|
250
|
+
const metaPath = this.collectionMetaPath(collection);
|
|
251
|
+
try {
|
|
252
|
+
await unlink(metaPath);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const maybeErr = error;
|
|
256
|
+
if (maybeErr?.code !== "ENOENT")
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
// rm({ force: true }) is a no-op on missing paths and still throws real errors.
|
|
260
|
+
await rm(this.collectionDir(collection), { recursive: true, force: true });
|
|
261
|
+
await rm(join(this.metaRoot, "history", collection), { recursive: true, force: true });
|
|
262
|
+
}
|
|
263
|
+
async getCollectionMetadata(collection) {
|
|
264
|
+
const filePath = this.collectionMetaPath(collection);
|
|
265
|
+
try {
|
|
266
|
+
const raw = await readFile(filePath, "utf8");
|
|
267
|
+
const parsed = JSON.parse(raw);
|
|
268
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
269
|
+
return null;
|
|
270
|
+
return parsed;
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async listCollectionMetadata() {
|
|
277
|
+
const dir = this.collectionsMetaDir();
|
|
278
|
+
try {
|
|
279
|
+
const files = await readdir(dir, { withFileTypes: true });
|
|
280
|
+
const metadataFiles = files
|
|
281
|
+
.filter((item) => item.isFile() && item.name.endsWith(".json"))
|
|
282
|
+
.map((item) => item.name.slice(0, -".json".length));
|
|
283
|
+
const metadata = await Promise.all(metadataFiles.map((name) => this.getCollectionMetadata(name)));
|
|
284
|
+
return metadata.filter((m) => Boolean(m));
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return [];
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=filesystem-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-adapter.js","sourceRoot":"","sources":["../../../src/runtime/storage/filesystem-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { UploadHandler } from "../../types.js";
|
|
2
|
+
export declare class FilesystemUploadHandler implements UploadHandler {
|
|
3
|
+
private readonly uploadsDir;
|
|
4
|
+
constructor(options?: {
|
|
5
|
+
uploadsDir?: string;
|
|
6
|
+
});
|
|
7
|
+
upload(file: File): Promise<{
|
|
8
|
+
url: string;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=filesystem-upload-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-upload-handler.d.ts","sourceRoot":"","sources":["../../../src/runtime/storage/filesystem-upload-handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAOpD,qBAAa,uBAAwB,YAAW,aAAa;IAC3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAIvC,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAanD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { IMAGE_KIND_TO_EXT, readAndValidateImage, sanitizeImageBaseName, } from "./image-validation.js";
|
|
4
|
+
export class FilesystemUploadHandler {
|
|
5
|
+
uploadsDir;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.uploadsDir = options?.uploadsDir ?? join(process.cwd(), "public", "uploads");
|
|
8
|
+
}
|
|
9
|
+
async upload(file) {
|
|
10
|
+
const { kind, bytes } = await readAndValidateImage(file);
|
|
11
|
+
const timestamp = Date.now().toString(36);
|
|
12
|
+
const stem = sanitizeImageBaseName(file.name) || `upload-${timestamp}`;
|
|
13
|
+
const ext = IMAGE_KIND_TO_EXT[kind];
|
|
14
|
+
const filename = `${timestamp}-${stem}.${ext}`;
|
|
15
|
+
await mkdir(this.uploadsDir, { recursive: true });
|
|
16
|
+
await writeFile(join(this.uploadsDir, filename), bytes);
|
|
17
|
+
return { url: `/uploads/${filename}` };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=filesystem-upload-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-upload-handler.js","sourceRoot":"","sources":["../../../src/runtime/storage/filesystem-upload-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,OAAO,uBAAuB;IACjB,UAAU,CAAS;IAEpC,YAAY,OAAiC;QAC3C,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACpF,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAU;QACrB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAEzD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,SAAS,EAAE,CAAC;QACvE,MAAM,GAAG,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,GAAG,SAAS,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;QAE/C,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;QAExD,OAAO,EAAE,GAAG,EAAE,YAAY,QAAQ,EAAE,EAAE,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared image-upload validation primitives. Used by both the filesystem
|
|
3
|
+
* upload handler and adapter implementations (Cloudflare R2, etc.) so every
|
|
4
|
+
* handler treats the browser-supplied `file.type` as a hint and the bytes on
|
|
5
|
+
* disk as authoritative.
|
|
6
|
+
*/
|
|
7
|
+
export type AllowedImageKind = "jpeg" | "png" | "webp" | "avif";
|
|
8
|
+
export declare const IMAGE_MIME_TO_KIND: Record<string, AllowedImageKind>;
|
|
9
|
+
export declare const IMAGE_KIND_TO_EXT: Record<AllowedImageKind, string>;
|
|
10
|
+
export declare const IMAGE_MAX_SIZE: number;
|
|
11
|
+
export declare function detectImageKind(bytes: Uint8Array): AllowedImageKind | null;
|
|
12
|
+
/**
|
|
13
|
+
* Strip the client-supplied extension entirely; callers re-derive it from the
|
|
14
|
+
* verified magic bytes. Blocks `evil.html` / `evil.svg` filenames from
|
|
15
|
+
* landing in storage even when their byte contents happen to match a
|
|
16
|
+
* permitted image format.
|
|
17
|
+
*/
|
|
18
|
+
export declare function sanitizeImageBaseName(name: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Common validation pipeline: enforces size cap, declared MIME allowlist,
|
|
21
|
+
* magic-byte match against the declared type, and returns the kind + buffered
|
|
22
|
+
* bytes so the caller can stream them to the destination once.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readAndValidateImage(file: File): Promise<{
|
|
25
|
+
kind: AllowedImageKind;
|
|
26
|
+
bytes: Uint8Array;
|
|
27
|
+
}>;
|
|
28
|
+
//# sourceMappingURL=image-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-validation.d.ts","sourceRoot":"","sources":["../../../src/runtime/storage/image-validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhE,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAK/D,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAK9D,CAAC;AAEF,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,gBAAgB,GAAG,IAAI,CA6C1E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;IAC9D,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,UAAU,CAAC;CACnB,CAAC,CAkBD"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared image-upload validation primitives. Used by both the filesystem
|
|
3
|
+
* upload handler and adapter implementations (Cloudflare R2, etc.) so every
|
|
4
|
+
* handler treats the browser-supplied `file.type` as a hint and the bytes on
|
|
5
|
+
* disk as authoritative.
|
|
6
|
+
*/
|
|
7
|
+
export const IMAGE_MIME_TO_KIND = {
|
|
8
|
+
"image/jpeg": "jpeg",
|
|
9
|
+
"image/png": "png",
|
|
10
|
+
"image/webp": "webp",
|
|
11
|
+
"image/avif": "avif",
|
|
12
|
+
};
|
|
13
|
+
export const IMAGE_KIND_TO_EXT = {
|
|
14
|
+
jpeg: "jpg",
|
|
15
|
+
png: "png",
|
|
16
|
+
webp: "webp",
|
|
17
|
+
avif: "avif",
|
|
18
|
+
};
|
|
19
|
+
export const IMAGE_MAX_SIZE = 10 * 1024 * 1024;
|
|
20
|
+
export function detectImageKind(bytes) {
|
|
21
|
+
if (bytes.length < 12)
|
|
22
|
+
return null;
|
|
23
|
+
if (bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
|
|
24
|
+
return "jpeg";
|
|
25
|
+
if (bytes[0] === 0x89 &&
|
|
26
|
+
bytes[1] === 0x50 &&
|
|
27
|
+
bytes[2] === 0x4e &&
|
|
28
|
+
bytes[3] === 0x47 &&
|
|
29
|
+
bytes[4] === 0x0d &&
|
|
30
|
+
bytes[5] === 0x0a &&
|
|
31
|
+
bytes[6] === 0x1a &&
|
|
32
|
+
bytes[7] === 0x0a) {
|
|
33
|
+
return "png";
|
|
34
|
+
}
|
|
35
|
+
if (bytes[0] === 0x52 &&
|
|
36
|
+
bytes[1] === 0x49 &&
|
|
37
|
+
bytes[2] === 0x46 &&
|
|
38
|
+
bytes[3] === 0x46 &&
|
|
39
|
+
bytes[8] === 0x57 &&
|
|
40
|
+
bytes[9] === 0x45 &&
|
|
41
|
+
bytes[10] === 0x42 &&
|
|
42
|
+
bytes[11] === 0x50) {
|
|
43
|
+
return "webp";
|
|
44
|
+
}
|
|
45
|
+
if (bytes[4] === 0x66 &&
|
|
46
|
+
bytes[5] === 0x74 &&
|
|
47
|
+
bytes[6] === 0x79 &&
|
|
48
|
+
bytes[7] === 0x70 &&
|
|
49
|
+
bytes[8] === 0x61 &&
|
|
50
|
+
bytes[9] === 0x76 &&
|
|
51
|
+
bytes[10] === 0x69 &&
|
|
52
|
+
(bytes[11] === 0x66 || bytes[11] === 0x73)) {
|
|
53
|
+
return "avif";
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Strip the client-supplied extension entirely; callers re-derive it from the
|
|
59
|
+
* verified magic bytes. Blocks `evil.html` / `evil.svg` filenames from
|
|
60
|
+
* landing in storage even when their byte contents happen to match a
|
|
61
|
+
* permitted image format.
|
|
62
|
+
*/
|
|
63
|
+
export function sanitizeImageBaseName(name) {
|
|
64
|
+
const dot = name.lastIndexOf(".");
|
|
65
|
+
const stem = dot > 0 ? name.slice(0, dot) : name;
|
|
66
|
+
return stem
|
|
67
|
+
.replace(/[^a-zA-Z0-9_-]/g, "-")
|
|
68
|
+
.replace(/-+/g, "-")
|
|
69
|
+
.replace(/^-+|-+$/g, "")
|
|
70
|
+
.slice(0, 80);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Common validation pipeline: enforces size cap, declared MIME allowlist,
|
|
74
|
+
* magic-byte match against the declared type, and returns the kind + buffered
|
|
75
|
+
* bytes so the caller can stream them to the destination once.
|
|
76
|
+
*/
|
|
77
|
+
export async function readAndValidateImage(file) {
|
|
78
|
+
if (!IMAGE_MIME_TO_KIND[file.type]) {
|
|
79
|
+
throw new Error("File type not allowed");
|
|
80
|
+
}
|
|
81
|
+
if (file.size > IMAGE_MAX_SIZE) {
|
|
82
|
+
throw new Error("File too large (max 10MB)");
|
|
83
|
+
}
|
|
84
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
85
|
+
const detected = detectImageKind(bytes);
|
|
86
|
+
if (!detected) {
|
|
87
|
+
throw new Error("File contents do not match a supported image format");
|
|
88
|
+
}
|
|
89
|
+
if (detected !== IMAGE_MIME_TO_KIND[file.type]) {
|
|
90
|
+
throw new Error("File contents do not match the declared MIME type");
|
|
91
|
+
}
|
|
92
|
+
return { kind: detected, bytes };
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=image-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-validation.js","sourceRoot":"","sources":["../../../src/runtime/storage/image-validation.ts"],"names":[],"mappings":"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"}
|