@micacms/runtime 0.5.0-rc.2 → 0.6.0-rc.1

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 (95) hide show
  1. package/README.md +3 -1
  2. package/dist/app.js +1 -1
  3. package/dist/app.js.map +1 -1
  4. package/dist/config.d.ts +13 -13
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +9 -9
  7. package/dist/config.js.map +1 -1
  8. package/dist/context.d.ts +2 -0
  9. package/dist/context.d.ts.map +1 -1
  10. package/dist/context.js +9 -3
  11. package/dist/context.js.map +1 -1
  12. package/dist/http.d.ts +1 -3
  13. package/dist/http.d.ts.map +1 -1
  14. package/dist/http.js +2 -9
  15. package/dist/http.js.map +1 -1
  16. package/dist/index.d.ts +3 -3
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +3 -3
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/errors.d.ts +1 -2
  21. package/dist/lib/errors.d.ts.map +1 -1
  22. package/dist/lib/errors.js +0 -2
  23. package/dist/lib/errors.js.map +1 -1
  24. package/dist/lib/id.d.ts +0 -10
  25. package/dist/lib/id.d.ts.map +1 -1
  26. package/dist/lib/id.js +0 -50
  27. package/dist/lib/id.js.map +1 -1
  28. package/dist/lib/rate-limit.d.ts +0 -2
  29. package/dist/lib/rate-limit.d.ts.map +1 -1
  30. package/dist/lib/rate-limit.js +0 -3
  31. package/dist/lib/rate-limit.js.map +1 -1
  32. package/dist/middleware/index.d.ts +1 -1
  33. package/dist/middleware/index.js +2 -2
  34. package/dist/middleware/index.js.map +1 -1
  35. package/dist/routes/admin-content.d.ts.map +1 -1
  36. package/dist/routes/admin-content.js +4 -3
  37. package/dist/routes/admin-content.js.map +1 -1
  38. package/dist/routes/admin-media.d.ts.map +1 -1
  39. package/dist/routes/admin-media.js +6 -1
  40. package/dist/routes/admin-media.js.map +1 -1
  41. package/dist/routes/admin-system.d.ts.map +1 -1
  42. package/dist/routes/admin-system.js +6 -4
  43. package/dist/routes/admin-system.js.map +1 -1
  44. package/dist/schema.d.ts +15 -3
  45. package/dist/schema.d.ts.map +1 -1
  46. package/dist/schema.js +63 -10
  47. package/dist/schema.js.map +1 -1
  48. package/dist/services/changes.d.ts.map +1 -1
  49. package/dist/services/changes.js +89 -13
  50. package/dist/services/changes.js.map +1 -1
  51. package/dist/services/content-store.d.ts +6 -1
  52. package/dist/services/content-store.d.ts.map +1 -1
  53. package/dist/services/content-store.js +25 -15
  54. package/dist/services/content-store.js.map +1 -1
  55. package/dist/services/staging.d.ts +2 -0
  56. package/dist/services/staging.d.ts.map +1 -1
  57. package/dist/services/staging.js +15 -4
  58. package/dist/services/staging.js.map +1 -1
  59. package/dist/workspace/tree.d.ts +6 -0
  60. package/dist/workspace/tree.d.ts.map +1 -1
  61. package/dist/workspace/tree.js +28 -0
  62. package/dist/workspace/tree.js.map +1 -1
  63. package/package.json +12 -4
  64. package/src/app.ts +127 -0
  65. package/src/config.ts +113 -0
  66. package/src/context.ts +167 -0
  67. package/src/http.ts +46 -0
  68. package/src/index.ts +141 -0
  69. package/src/lib/errors.ts +84 -0
  70. package/src/lib/file-type.ts +108 -0
  71. package/src/lib/id.ts +6 -0
  72. package/src/lib/logger.ts +87 -0
  73. package/src/lib/ports.ts +17 -0
  74. package/src/lib/rate-limit.ts +72 -0
  75. package/src/lib/schema-stats.ts +183 -0
  76. package/src/lib/version.ts +29 -0
  77. package/src/middleware/index.ts +234 -0
  78. package/src/routes/admin-content.ts +197 -0
  79. package/src/routes/admin-media.ts +96 -0
  80. package/src/routes/admin-spa.ts +82 -0
  81. package/src/routes/admin-staging.ts +126 -0
  82. package/src/routes/admin-system.ts +146 -0
  83. package/src/routes/health.ts +67 -0
  84. package/src/schema.ts +217 -0
  85. package/src/server.ts +68 -0
  86. package/src/services/changes.ts +605 -0
  87. package/src/services/content-store.ts +492 -0
  88. package/src/services/media.ts +229 -0
  89. package/src/services/staging.ts +330 -0
  90. package/src/workspace/developer.ts +46 -0
  91. package/src/workspace/files.ts +243 -0
  92. package/src/workspace/git.ts +108 -0
  93. package/src/workspace/locks.ts +103 -0
  94. package/src/workspace/policy.ts +77 -0
  95. package/src/workspace/tree.ts +314 -0
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @micacms/runtime
2
2
 
3
- > **Experimental.** Pre-1.0: the API can still change without notice.
3
+ > **Internal.** Published only because the Mica apps install it, and pinned
4
+ > exactly by them. It has no semver promise: any release can break its API. Use
5
+ > `@micacms/core`, `@micacms/astro` or the `@micacms/editor` CLI instead.
4
6
 
5
7
  The single-site Mica runtime: the Hono API, application services, the content
6
8
  files and the Git working-tree code (Git runner, locks, owner policy), deliberately **authentication-neutral**. It answers "who is
package/dist/app.js CHANGED
@@ -58,7 +58,7 @@ export function createApp(ctx, options = {}) {
58
58
  app.route("/api/admin", adminStagingRoutes());
59
59
  app.route("/api/admin/media", adminMediaRoutes());
60
60
  app.all("/api/*", (c) => c.json({ error: { code: "not_found", message: "No such endpoint." } }, 404));
61
- const adminDir = options.adminDir ?? ctx.config.ADMIN_DIR;
61
+ const adminDir = options.adminDir ?? ctx.config.MICA_ADMIN_DIR;
62
62
  if (adminDir && adminAssetsExist(adminDir)) {
63
63
  app.route("/admin", adminSpaRoutes(adminDir));
64
64
  }
package/dist/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Assembles the app: health, the admin API and the admin SPA. There is no\n * public content API and no `/media/*`; a website builds from the repository,\n * not from this server. Authentication comes from the composing app.\n */\n\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport type { MiddlewareHandler } from \"hono\";\n\nimport type { RuntimeContext } from \"./context.js\";\nimport type { Env } from \"./http.js\";\nimport {\n allowedOrigins,\n errorHandler,\n originGuard,\n requestSizeLimit,\n rateLimit,\n requestContext,\n securityHeaders,\n} from \"./middleware/index.js\";\nimport { adminContentRoutes } from \"./routes/admin-content.js\";\nimport { adminMediaRoutes } from \"./routes/admin-media.js\";\nimport { adminSpaRoutes, adminAssetsExist } from \"./routes/admin-spa.js\";\nimport { adminStagingRoutes } from \"./routes/admin-staging.js\";\nimport { adminSystemRoutes } from \"./routes/admin-system.js\";\nimport { healthRoutes } from \"./routes/health.js\";\n\n/** What a composing app contributes around the auth-neutral runtime. */\nexport interface AuthSurface {\n /** Mounted at `/api/auth`: sign-in, callback, logout, whatever auth needs. */\n routes: Hono<Env>;\n /** Applied before every `/api/admin` route. Must set `access` or refuse the request. */\n gate: MiddlewareHandler<Env>[];\n}\n\n/**\n * Allows every request with no identity. Safe only because the local editor is\n * loopback-bound and Host-guarded.\n */\nexport function allowAll(): MiddlewareHandler<Env> {\n return async (c, next) => {\n c.set(\"access\", { identity: null });\n await next();\n };\n}\n\n/** `/api/auth/config` with nothing behind it: the SPA's cue to skip sign-in. */\nfunction unauthenticatedSurface(): AuthSurface {\n const routes = new Hono<Env>();\n routes.get(\"/config\", (c) => c.json({ authRequired: false, provider: null }));\n return { routes, gate: [allowAll()] };\n}\n\nexport interface CreateAppOptions {\n /** Absent means the unauthenticated local surface. */\n auth?: AuthSurface;\n /**\n * Middleware in front of everything, so the local editor's Host allowlist\n * also guards the health endpoints.\n */\n guards?: MiddlewareHandler<Env>[];\n adminDir?: string;\n}\n\nexport function createApp(ctx: RuntimeContext, options: CreateAppOptions = {}): Hono<Env> {\n const app = new Hono<Env>();\n const auth = options.auth ?? unauthenticatedSurface();\n\n app.use(\"*\", requestContext(ctx));\n for (const guard of options.guards ?? []) app.use(\"*\", guard);\n app.use(\"*\", securityHeaders());\n app.onError(errorHandler());\n\n app.route(\"/\", healthRoutes());\n\n // Only for the Vite dev server on another origin; in production the SPA is same-origin.\n if (ctx.config.adminOrigins.length > 0) {\n app.use(\n \"/api/*\",\n cors({\n origin: allowedOrigins(ctx.config),\n credentials: true,\n allowHeaders: [\"content-type\", \"x-csrf-token\"],\n allowMethods: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n }),\n );\n }\n\n app.use(\"/api/*\", rateLimit());\n app.use(\"/api/*\", originGuard());\n app.use(\"/api/*\", requestSizeLimit());\n\n app.route(\"/api/auth\", auth.routes);\n\n // One gate and no roles: whoever the surface admits is an editor of this site.\n for (const middleware of auth.gate) app.use(\"/api/admin/*\", middleware);\n\n app.route(\"/api/admin\", adminSystemRoutes());\n app.route(\"/api/admin\", adminContentRoutes());\n app.route(\"/api/admin\", adminStagingRoutes());\n app.route(\"/api/admin/media\", adminMediaRoutes());\n\n app.all(\"/api/*\", (c) =>\n c.json({ error: { code: \"not_found\" as const, message: \"No such endpoint.\" } }, 404),\n );\n\n const adminDir = options.adminDir ?? ctx.config.ADMIN_DIR;\n\n if (adminDir && adminAssetsExist(adminDir)) {\n app.route(\"/admin\", adminSpaRoutes(adminDir));\n } else {\n app.get(\"/admin/*\", (c) =>\n c.text(\n \"The admin interface has not been built.\\n\" +\n \"Run `rush build`, or use `rush dev` and open the Vite dev server.\",\n 503,\n ),\n );\n }\n\n app.get(\"/\", (c) => c.redirect(\"/admin/\"));\n\n app.notFound((c) => c.text(\"Not found\", 404));\n\n return app;\n}\n"]}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Assembles the app: health, the admin API and the admin SPA. There is no\n * public content API and no `/media/*`; a website builds from the repository,\n * not from this server. Authentication comes from the composing app.\n */\n\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport type { MiddlewareHandler } from \"hono\";\n\nimport type { RuntimeContext } from \"./context.js\";\nimport type { Env } from \"./http.js\";\nimport {\n allowedOrigins,\n errorHandler,\n originGuard,\n requestSizeLimit,\n rateLimit,\n requestContext,\n securityHeaders,\n} from \"./middleware/index.js\";\nimport { adminContentRoutes } from \"./routes/admin-content.js\";\nimport { adminMediaRoutes } from \"./routes/admin-media.js\";\nimport { adminSpaRoutes, adminAssetsExist } from \"./routes/admin-spa.js\";\nimport { adminStagingRoutes } from \"./routes/admin-staging.js\";\nimport { adminSystemRoutes } from \"./routes/admin-system.js\";\nimport { healthRoutes } from \"./routes/health.js\";\n\n/** What a composing app contributes around the auth-neutral runtime. */\nexport interface AuthSurface {\n /** Mounted at `/api/auth`: sign-in, callback, logout, whatever auth needs. */\n routes: Hono<Env>;\n /** Applied before every `/api/admin` route. Must set `access` or refuse the request. */\n gate: MiddlewareHandler<Env>[];\n}\n\n/**\n * Allows every request with no identity. Safe only because the local editor is\n * loopback-bound and Host-guarded.\n */\nexport function allowAll(): MiddlewareHandler<Env> {\n return async (c, next) => {\n c.set(\"access\", { identity: null });\n await next();\n };\n}\n\n/** `/api/auth/config` with nothing behind it: the SPA's cue to skip sign-in. */\nfunction unauthenticatedSurface(): AuthSurface {\n const routes = new Hono<Env>();\n routes.get(\"/config\", (c) => c.json({ authRequired: false, provider: null }));\n return { routes, gate: [allowAll()] };\n}\n\nexport interface CreateAppOptions {\n /** Absent means the unauthenticated local surface. */\n auth?: AuthSurface;\n /**\n * Middleware in front of everything, so the local editor's Host allowlist\n * also guards the health endpoints.\n */\n guards?: MiddlewareHandler<Env>[];\n adminDir?: string;\n}\n\nexport function createApp(ctx: RuntimeContext, options: CreateAppOptions = {}): Hono<Env> {\n const app = new Hono<Env>();\n const auth = options.auth ?? unauthenticatedSurface();\n\n app.use(\"*\", requestContext(ctx));\n for (const guard of options.guards ?? []) app.use(\"*\", guard);\n app.use(\"*\", securityHeaders());\n app.onError(errorHandler());\n\n app.route(\"/\", healthRoutes());\n\n // Only for the Vite dev server on another origin; in production the SPA is same-origin.\n if (ctx.config.adminOrigins.length > 0) {\n app.use(\n \"/api/*\",\n cors({\n origin: allowedOrigins(ctx.config),\n credentials: true,\n allowHeaders: [\"content-type\", \"x-csrf-token\"],\n allowMethods: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n }),\n );\n }\n\n app.use(\"/api/*\", rateLimit());\n app.use(\"/api/*\", originGuard());\n app.use(\"/api/*\", requestSizeLimit());\n\n app.route(\"/api/auth\", auth.routes);\n\n // One gate and no roles: whoever the surface admits is an editor of this site.\n for (const middleware of auth.gate) app.use(\"/api/admin/*\", middleware);\n\n app.route(\"/api/admin\", adminSystemRoutes());\n app.route(\"/api/admin\", adminContentRoutes());\n app.route(\"/api/admin\", adminStagingRoutes());\n app.route(\"/api/admin/media\", adminMediaRoutes());\n\n app.all(\"/api/*\", (c) =>\n c.json({ error: { code: \"not_found\" as const, message: \"No such endpoint.\" } }, 404),\n );\n\n const adminDir = options.adminDir ?? ctx.config.MICA_ADMIN_DIR;\n\n if (adminDir && adminAssetsExist(adminDir)) {\n app.route(\"/admin\", adminSpaRoutes(adminDir));\n } else {\n app.get(\"/admin/*\", (c) =>\n c.text(\n \"The admin interface has not been built.\\n\" +\n \"Run `rush build`, or use `rush dev` and open the Vite dev server.\",\n 503,\n ),\n );\n }\n\n app.get(\"/\", (c) => c.redirect(\"/admin/\"));\n\n app.notFound((c) => c.text(\"Not found\", 404));\n\n return app;\n}\n"]}
package/dist/config.d.ts CHANGED
@@ -25,7 +25,7 @@ export declare const runtimeEnv: {
25
25
  test: "test";
26
26
  production: "production";
27
27
  }>>;
28
- LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
28
+ MICA_LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
29
29
  debug: "debug";
30
30
  info: "info";
31
31
  warn: "warn";
@@ -37,8 +37,8 @@ export declare const runtimeEnv: {
37
37
  PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
38
38
  /** Public origin of the CMS itself. Used for cookies, CORS and media URLs. */
39
39
  MICA_PUBLIC_URL: z.ZodDefault<z.ZodString>;
40
- /** The content root this Mica serves: a directory in a Git working tree. */
41
- CONTENT_ROOT: z.ZodDefault<z.ZodString>;
40
+ /** The content root this Mica serves: a directory in a Git working tree. Each app sets it. */
41
+ MICA_CONTENT_ROOT: z.ZodString;
42
42
  /** `false` disables commit and push: Publish shows the pending changes read-only. */
43
43
  MICA_LOCAL_PUBLISH: z.ZodDefault<z.ZodPipe<z.ZodEnum<{
44
44
  0: "0";
@@ -46,18 +46,18 @@ export declare const runtimeEnv: {
46
46
  true: "true";
47
47
  false: "false";
48
48
  }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>;
49
- MAX_UPLOAD_BYTES: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
49
+ MICA_MAX_UPLOAD_BYTES: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
50
50
  /** Read the client IP from X-Forwarded-For. Only enable behind a proxy. */
51
- TRUST_PROXY: z.ZodDefault<z.ZodPipe<z.ZodEnum<{
51
+ MICA_TRUST_PROXY: z.ZodDefault<z.ZodPipe<z.ZodEnum<{
52
52
  0: "0";
53
53
  1: "1";
54
54
  true: "true";
55
55
  false: "false";
56
56
  }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>;
57
57
  /** Extra browser origins allowed to call the admin API (dev Vite server). */
58
- ADMIN_ORIGINS: z.ZodOptional<z.ZodString>;
58
+ MICA_ADMIN_ORIGINS: z.ZodOptional<z.ZodString>;
59
59
  /** Overrides where the built admin SPA is served from. */
60
- ADMIN_DIR: z.ZodOptional<z.ZodString>;
60
+ MICA_ADMIN_DIR: z.ZodOptional<z.ZodString>;
61
61
  };
62
62
  declare const runtimeSchema: z.ZodObject<{
63
63
  NODE_ENV: z.ZodDefault<z.ZodEnum<{
@@ -65,7 +65,7 @@ declare const runtimeSchema: z.ZodObject<{
65
65
  test: "test";
66
66
  production: "production";
67
67
  }>>;
68
- LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
68
+ MICA_LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
69
69
  debug: "debug";
70
70
  info: "info";
71
71
  warn: "warn";
@@ -75,22 +75,22 @@ declare const runtimeSchema: z.ZodObject<{
75
75
  HOST: z.ZodOptional<z.ZodString>;
76
76
  PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
77
77
  MICA_PUBLIC_URL: z.ZodDefault<z.ZodString>;
78
- CONTENT_ROOT: z.ZodDefault<z.ZodString>;
78
+ MICA_CONTENT_ROOT: z.ZodString;
79
79
  MICA_LOCAL_PUBLISH: z.ZodDefault<z.ZodPipe<z.ZodEnum<{
80
80
  0: "0";
81
81
  1: "1";
82
82
  true: "true";
83
83
  false: "false";
84
84
  }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>;
85
- MAX_UPLOAD_BYTES: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
86
- TRUST_PROXY: z.ZodDefault<z.ZodPipe<z.ZodEnum<{
85
+ MICA_MAX_UPLOAD_BYTES: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
86
+ MICA_TRUST_PROXY: z.ZodDefault<z.ZodPipe<z.ZodEnum<{
87
87
  0: "0";
88
88
  1: "1";
89
89
  true: "true";
90
90
  false: "false";
91
91
  }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>;
92
- ADMIN_ORIGINS: z.ZodOptional<z.ZodString>;
93
- ADMIN_DIR: z.ZodOptional<z.ZodString>;
92
+ MICA_ADMIN_ORIGINS: z.ZodOptional<z.ZodString>;
93
+ MICA_ADMIN_DIR: z.ZodOptional<z.ZodString>;
94
94
  }, z.core.$strip>;
95
95
  export type RuntimeEnv = z.infer<typeof runtimeSchema>;
96
96
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,2EAA2E;AAC3E,eAAO,MAAM,IAAI,GAAI,UAAU,OAAO;;;;;2DAIhB,CAAC;AAEvB,yEAAyE;AACzE,eAAO,MAAM,IAAI,GAAI,OAAO,MAAM,GAAG,SAAS,KAAG,MAAM,EAInC,CAAC;AAErB,qBAAa,WAAY,SAAQ,KAAK;IAC3B,IAAI,SAAiB;CAC/B;AAED,yDAAyD;AACzD,eAAO,MAAM,QAAQ,aAAyE,CAAC;AAE/F,kFAAkF;AAClF,eAAO,MAAM,UAAU;;;;;;;;;;;;;IAIrB,oDAAoD;;;IAIpD,8EAA8E;;IAG9E,4EAA4E;;IAG5E,qFAAqF;;;;;;;;IASrF,2EAA2E;;;;;;;IAE3E,6EAA6E;;IAE7E,0DAA0D;;CAE3D,CAAC;AAEF,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAuB,CAAC;AAC3C,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,UAAU,EAAE,GAAG,EAAE,CAAC;;;;EAOzD;AAED,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,UAAU,CAAC,CAAC,GAAG;IACzE,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,WAAW,IAAI,IAAI,CAUlC;AAED,mGAAmG;AACnG,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAIvE;AAED,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CAK9D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,2EAA2E;AAC3E,eAAO,MAAM,IAAI,GAAI,UAAU,OAAO;;;;;2DAIhB,CAAC;AAEvB,yEAAyE;AACzE,eAAO,MAAM,IAAI,GAAI,OAAO,MAAM,GAAG,SAAS,KAAG,MAAM,EAInC,CAAC;AAErB,qBAAa,WAAY,SAAQ,KAAK;IAC3B,IAAI,SAAiB;CAC/B;AAED,yDAAyD;AACzD,eAAO,MAAM,QAAQ,aAAyE,CAAC;AAE/F,kFAAkF;AAClF,eAAO,MAAM,UAAU;;;;;;;;;;;;;IAIrB,oDAAoD;;;IAIpD,8EAA8E;;IAG9E,8FAA8F;;IAG9F,qFAAqF;;;;;;;;IASrF,2EAA2E;;;;;;;IAE3E,6EAA6E;;IAE7E,0DAA0D;;CAE3D,CAAC;AAEF,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAuB,CAAC;AAC3C,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,UAAU,EAAE,GAAG,EAAE,CAAC;;;;EAOzD;AAED,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,UAAU,CAAC,CAAC,GAAG;IACzE,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,WAAW,IAAI,IAAI,CAUlC;AAED,mGAAmG;AACnG,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAIvE;AAED,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CAK9D"}
package/dist/config.js CHANGED
@@ -24,27 +24,27 @@ export const LOOPBACK = new Set(["localhost", "127.0.0.1", "[::1]", "::1", "0:0:
24
24
  /** The variables the runtime itself reads. Apps spread this into their schema. */
25
25
  export const runtimeEnv = {
26
26
  NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
27
- LOG_LEVEL: z.enum(["debug", "info", "warn", "error", "silent"]).default("info"),
27
+ MICA_LOG_LEVEL: z.enum(["debug", "info", "warn", "error", "silent"]).default("info"),
28
28
  /** The listen address. The app sets the default. */
29
29
  HOST: z.string().optional(),
30
30
  PORT: z.coerce.number().int().min(1).max(65535).default(DEFAULT_PORT),
31
31
  /** Public origin of the CMS itself. Used for cookies, CORS and media URLs. */
32
32
  MICA_PUBLIC_URL: z.string().url().default(DEFAULT_BASE_URL),
33
- /** The content root this Mica serves: a directory in a Git working tree. */
34
- CONTENT_ROOT: z.string().default("../../examples/muster-consulting/content"),
33
+ /** The content root this Mica serves: a directory in a Git working tree. Each app sets it. */
34
+ MICA_CONTENT_ROOT: z.string(),
35
35
  /** `false` disables commit and push: Publish shows the pending changes read-only. */
36
36
  MICA_LOCAL_PUBLISH: bool(true),
37
- MAX_UPLOAD_BYTES: z.coerce
37
+ MICA_MAX_UPLOAD_BYTES: z.coerce
38
38
  .number()
39
39
  .int()
40
40
  .positive()
41
41
  .default(25 * 1024 * 1024),
42
42
  /** Read the client IP from X-Forwarded-For. Only enable behind a proxy. */
43
- TRUST_PROXY: bool(false),
43
+ MICA_TRUST_PROXY: bool(false),
44
44
  /** Extra browser origins allowed to call the admin API (dev Vite server). */
45
- ADMIN_ORIGINS: z.string().optional(),
45
+ MICA_ADMIN_ORIGINS: z.string().optional(),
46
46
  /** Overrides where the built admin SPA is served from. */
47
- ADMIN_DIR: z.string().optional(),
47
+ MICA_ADMIN_DIR: z.string().optional(),
48
48
  };
49
49
  const runtimeSchema = z.object(runtimeEnv);
50
50
  /**
@@ -55,8 +55,8 @@ export function deriveRuntime(env) {
55
55
  return {
56
56
  ...env,
57
57
  isProduction: env.NODE_ENV === "production",
58
- contentRoot: path.resolve(env.CONTENT_ROOT),
59
- adminOrigins: list(env.ADMIN_ORIGINS),
58
+ contentRoot: path.resolve(env.MICA_CONTENT_ROOT),
59
+ adminOrigins: list(env.MICA_ADMIN_ORIGINS),
60
60
  };
61
61
  }
62
62
  /** Loads `./.env` into `process.env`. Variables that are already set win. */
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The environment variables the runtime reads. There is no `loadConfig`: each\n * app builds its own schema from `runtimeEnv`, because the variables and\n * defaults are what make one app different from another.\n */\n\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { DEFAULT_BASE_URL, DEFAULT_PORT } from \"./lib/ports.js\";\n\n/** In Zod 4, `.default` takes the output value and skips the transform. */\nexport const bool = (fallback: boolean) =>\n z\n .enum([\"true\", \"false\", \"1\", \"0\"])\n .transform((value) => value === \"true\" || value === \"1\")\n .default(fallback);\n\n/** Splits a comma-separated list, trims entries and drops empty ones. */\nexport const list = (value: string | undefined): string[] =>\n (value ?? \"\")\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n\nexport class ConfigError extends Error {\n override name = \"ConfigError\";\n}\n\n/** Hostnames that are this machine, and nowhere else. */\nexport const LOOPBACK = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\", \"::1\", \"0:0:0:0:0:0:0:1\"]);\n\n/** The variables the runtime itself reads. Apps spread this into their schema. */\nexport const runtimeEnv = {\n NODE_ENV: z.enum([\"development\", \"test\", \"production\"]).default(\"development\"),\n LOG_LEVEL: z.enum([\"debug\", \"info\", \"warn\", \"error\", \"silent\"]).default(\"info\"),\n\n /** The listen address. The app sets the default. */\n HOST: z.string().optional(),\n PORT: z.coerce.number().int().min(1).max(65535).default(DEFAULT_PORT),\n\n /** Public origin of the CMS itself. Used for cookies, CORS and media URLs. */\n MICA_PUBLIC_URL: z.string().url().default(DEFAULT_BASE_URL),\n\n /** The content root this Mica serves: a directory in a Git working tree. */\n CONTENT_ROOT: z.string().default(\"../../examples/muster-consulting/content\"),\n\n /** `false` disables commit and push: Publish shows the pending changes read-only. */\n MICA_LOCAL_PUBLISH: bool(true),\n\n MAX_UPLOAD_BYTES: z.coerce\n .number()\n .int()\n .positive()\n .default(25 * 1024 * 1024),\n\n /** Read the client IP from X-Forwarded-For. Only enable behind a proxy. */\n TRUST_PROXY: bool(false),\n /** Extra browser origins allowed to call the admin API (dev Vite server). */\n ADMIN_ORIGINS: z.string().optional(),\n /** Overrides where the built admin SPA is served from. */\n ADMIN_DIR: z.string().optional(),\n};\n\nconst runtimeSchema = z.object(runtimeEnv);\nexport type RuntimeEnv = z.infer<typeof runtimeSchema>;\n\n/**\n * Adds the derived values to the parsed environment. Each app calls it from its\n * own `transform`.\n */\nexport function deriveRuntime<T extends RuntimeEnv>(env: T) {\n return {\n ...env,\n isProduction: env.NODE_ENV === \"production\",\n contentRoot: path.resolve(env.CONTENT_ROOT),\n adminOrigins: list(env.ADMIN_ORIGINS),\n };\n}\n\n/** What the runtime requires of a finished configuration. */\nexport type RuntimeConfig = ReturnType<typeof deriveRuntime<RuntimeEnv>> & {\n /** Resolved by the app: the runtime never guesses an address. */\n HOST: string;\n};\n\n/** Loads `./.env` into `process.env`. Variables that are already set win. */\nexport function loadEnvFile(): void {\n try {\n process.loadEnvFile();\n } catch (error) {\n // A missing `.env` is normal; an unreadable or malformed one must not start\n // the server with defaults.\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw new ConfigError(`Could not read .env: ${(error as Error).message}`);\n }\n }\n}\n\n/** Treats a blank variable as unset, so a verbatim copy of `.env.example` with blanks is valid. */\nexport function withoutBlanks(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n return Object.fromEntries(\n Object.entries(env).filter(([, value]) => value !== undefined && value.trim() !== \"\"),\n );\n}\n\n/** Formats a Zod refusal the way a person at a terminal reads it. */\nexport function describeConfigIssues(error: z.ZodError): string {\n const details = error.issues\n .map((issue) => ` ${issue.path.join(\".\") || \"(root)\"}: ${issue.message}`)\n .join(\"\\n\");\n return `Invalid environment configuration:\\n${details}`;\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The environment variables the runtime reads. There is no `loadConfig`: each\n * app builds its own schema from `runtimeEnv`, because the variables and\n * defaults are what make one app different from another.\n */\n\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { DEFAULT_BASE_URL, DEFAULT_PORT } from \"./lib/ports.js\";\n\n/** In Zod 4, `.default` takes the output value and skips the transform. */\nexport const bool = (fallback: boolean) =>\n z\n .enum([\"true\", \"false\", \"1\", \"0\"])\n .transform((value) => value === \"true\" || value === \"1\")\n .default(fallback);\n\n/** Splits a comma-separated list, trims entries and drops empty ones. */\nexport const list = (value: string | undefined): string[] =>\n (value ?? \"\")\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n\nexport class ConfigError extends Error {\n override name = \"ConfigError\";\n}\n\n/** Hostnames that are this machine, and nowhere else. */\nexport const LOOPBACK = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\", \"::1\", \"0:0:0:0:0:0:0:1\"]);\n\n/** The variables the runtime itself reads. Apps spread this into their schema. */\nexport const runtimeEnv = {\n NODE_ENV: z.enum([\"development\", \"test\", \"production\"]).default(\"development\"),\n MICA_LOG_LEVEL: z.enum([\"debug\", \"info\", \"warn\", \"error\", \"silent\"]).default(\"info\"),\n\n /** The listen address. The app sets the default. */\n HOST: z.string().optional(),\n PORT: z.coerce.number().int().min(1).max(65535).default(DEFAULT_PORT),\n\n /** Public origin of the CMS itself. Used for cookies, CORS and media URLs. */\n MICA_PUBLIC_URL: z.string().url().default(DEFAULT_BASE_URL),\n\n /** The content root this Mica serves: a directory in a Git working tree. Each app sets it. */\n MICA_CONTENT_ROOT: z.string(),\n\n /** `false` disables commit and push: Publish shows the pending changes read-only. */\n MICA_LOCAL_PUBLISH: bool(true),\n\n MICA_MAX_UPLOAD_BYTES: z.coerce\n .number()\n .int()\n .positive()\n .default(25 * 1024 * 1024),\n\n /** Read the client IP from X-Forwarded-For. Only enable behind a proxy. */\n MICA_TRUST_PROXY: bool(false),\n /** Extra browser origins allowed to call the admin API (dev Vite server). */\n MICA_ADMIN_ORIGINS: z.string().optional(),\n /** Overrides where the built admin SPA is served from. */\n MICA_ADMIN_DIR: z.string().optional(),\n};\n\nconst runtimeSchema = z.object(runtimeEnv);\nexport type RuntimeEnv = z.infer<typeof runtimeSchema>;\n\n/**\n * Adds the derived values to the parsed environment. Each app calls it from its\n * own `transform`.\n */\nexport function deriveRuntime<T extends RuntimeEnv>(env: T) {\n return {\n ...env,\n isProduction: env.NODE_ENV === \"production\",\n contentRoot: path.resolve(env.MICA_CONTENT_ROOT),\n adminOrigins: list(env.MICA_ADMIN_ORIGINS),\n };\n}\n\n/** What the runtime requires of a finished configuration. */\nexport type RuntimeConfig = ReturnType<typeof deriveRuntime<RuntimeEnv>> & {\n /** Resolved by the app: the runtime never guesses an address. */\n HOST: string;\n};\n\n/** Loads `./.env` into `process.env`. Variables that are already set win. */\nexport function loadEnvFile(): void {\n try {\n process.loadEnvFile();\n } catch (error) {\n // A missing `.env` is normal; an unreadable or malformed one must not start\n // the server with defaults.\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw new ConfigError(`Could not read .env: ${(error as Error).message}`);\n }\n }\n}\n\n/** Treats a blank variable as unset, so a verbatim copy of `.env.example` with blanks is valid. */\nexport function withoutBlanks(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n return Object.fromEntries(\n Object.entries(env).filter(([, value]) => value !== undefined && value.trim() !== \"\"),\n );\n}\n\n/** Formats a Zod refusal the way a person at a terminal reads it. */\nexport function describeConfigIssues(error: z.ZodError): string {\n const details = error.issues\n .map((issue) => ` ${issue.path.join(\".\") || \"(root)\"}: ${issue.message}`)\n .join(\"\\n\");\n return `Invalid environment configuration:\\n${details}`;\n}\n"]}
package/dist/context.d.ts CHANGED
@@ -27,6 +27,8 @@ export interface SiteContext {
27
27
  * drift gate compares against it.
28
28
  */
29
29
  schemaRevision: string | null;
30
+ /** Collections and singletons this build cannot write, from `readOnlyParts`, with the reason. */
31
+ readOnly: ReadonlyMap<string, string>;
30
32
  }
31
33
  export declare const DEFAULT_SITE_ID = "default";
32
34
  /** Facts about the composition that the System page shows and the runtime cannot know. */
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
package/dist/context.js CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { createLogger } from "./lib/logger.js";
7
7
  import { createRateLimiter } from "./lib/rate-limit.js";
8
+ import { readOnlyParts } from "./schema.js";
8
9
  import { createMediaService } from "./services/media.js";
9
10
  import { createContentStore } from "./services/content-store.js";
10
11
  import { createWorkingTreeStagingService } from "./services/staging.js";
@@ -14,29 +15,34 @@ export function createRuntimeContext(options) {
14
15
  const { config, model } = options;
15
16
  const logger = options.logger ??
16
17
  createLogger({
17
- level: config.LOG_LEVEL,
18
+ level: config.MICA_LOG_LEVEL,
18
19
  pretty: !config.isProduction,
19
20
  });
20
21
  const schemaRevision = options.schemaRevision ?? null;
21
22
  // Services write through the lock wrapper; staging takes the locks itself.
22
23
  const locks = options.locks ?? createWorkspaceLocks();
24
+ const readOnly = readOnlyParts(model);
25
+ for (const reason of readOnly.values())
26
+ logger.warn("Read-only in this version of Mica", { reason });
23
27
  const site = {
24
28
  id: options.siteId ?? DEFAULT_SITE_ID,
25
29
  model,
26
30
  contentRepository: lockedFiles(options.files, locks.tree),
27
31
  schemaRevision,
32
+ readOnly,
28
33
  };
29
34
  const services = {
30
- content: createContentStore(model, site.contentRepository),
35
+ content: createContentStore(model, site.contentRepository, readOnly),
31
36
  media: createMediaService({
32
37
  files: site.contentRepository,
33
- maxBytes: config.MAX_UPLOAD_BYTES,
38
+ maxBytes: config.MICA_MAX_UPLOAD_BYTES,
34
39
  }),
35
40
  staging: createWorkingTreeStagingService({
36
41
  model,
37
42
  files: options.files,
38
43
  contentRoot: config.contentRoot,
39
44
  schemaRevision,
45
+ readOnly,
40
46
  publishEnabled: config.MICA_LOCAL_PUBLISH,
41
47
  logger,
42
48
  locks,
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,YAAY,EAAe,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAoB,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAqB,MAAM,qBAAqB,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAqB,MAAM,6BAA6B,CAAC;AACpF,OAAO,EAAE,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAuB,MAAM,sBAAsB,CAAC;AAoB9F,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;AA6DzC,MAAM,UAAU,oBAAoB,CAAC,OAAoC;IACvE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAElC,MAAM,MAAM,GACV,OAAO,CAAC,MAAM;QACd,YAAY,CAAC;YACX,KAAK,EAAE,MAAM,CAAC,SAAS;YACvB,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY;SAC7B,CAAC,CAAC;IAEL,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC;IAEtD,2EAA2E;IAC3E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,oBAAoB,EAAE,CAAC;IAEtD,MAAM,IAAI,GAAgB;QACxB,EAAE,EAAE,OAAO,CAAC,MAAM,IAAI,eAAe;QACrC,KAAK;QACL,iBAAiB,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC;QACzD,cAAc;KACf,CAAC;IAEF,MAAM,QAAQ,GAAG;QACf,OAAO,EAAE,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,iBAAiB,CAAC;QAC1D,KAAK,EAAE,kBAAkB,CAAC;YACxB,KAAK,EAAE,IAAI,CAAC,iBAAiB;YAC7B,QAAQ,EAAE,MAAM,CAAC,gBAAgB;SAClC,CAAC;QACF,OAAO,EAAE,+BAA+B,CAAC;YACvC,KAAK;YACL,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,cAAc;YACd,cAAc,EAAE,MAAM,CAAC,kBAAkB;YACzC,MAAM;YACN,KAAK;YACL,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChD,CAAC;KACH,CAAC;IAEF,MAAM,QAAQ,GAAG;QACf,iEAAiE;QACjE,GAAG,EAAE,iBAAiB,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;KACzD,CAAC;IAEF,OAAO;QACL,MAAM;QACN,IAAI;QACJ,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,WAAW,EACT,OAAO,CAAC,WAAW;YACnB,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;QAE5B,KAAK,CAAC,QAAQ;YACZ,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACpB,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QAC/B,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Builds the runtime's services for one site, with no module-level singletons.\n * The app supplies the working tree's files and the owner policy, so the\n * runtime stays authentication-neutral.\n */\n\nimport type { Site } from \"@micacms/core\";\n\nimport type { RuntimeConfig } from \"./config.js\";\nimport { createLogger, type Logger } from \"./lib/logger.js\";\nimport { createRateLimiter, type RateLimiter } from \"./lib/rate-limit.js\";\nimport { createMediaService, type MediaService } from \"./services/media.js\";\nimport type { ContentStagingService } from \"./services/changes.js\";\nimport { createContentStore, type ContentStore } from \"./services/content-store.js\";\nimport { createWorkingTreeStagingService } from \"./services/staging.js\";\nimport { createWorkspaceLocks, lockedFiles, type WorkspaceLocks } from \"./workspace/locks.js\";\nimport type { OwnerPolicy } from \"./workspace/policy.js\";\nimport type { ContentFiles } from \"./workspace/files.js\";\nimport type { GitTree } from \"./workspace/tree.js\";\n\n/** The one site this process serves. Several sites means several processes. */\nexport interface SiteContext {\n /** Identifies this site in logs. */\n id: string;\n /** The content model, read from the repository at startup. */\n model: Site;\n /** The working tree's files behind the workspace locks. Only Publish makes a commit. */\n contentRepository: ContentFiles;\n /**\n * Revision of `.mica/schema.json` when the model was loaded. The publish\n * drift gate compares against it.\n */\n schemaRevision: string | null;\n}\n\nexport const DEFAULT_SITE_ID = \"default\";\n\n/** Facts about the composition that the System page shows and the runtime cannot know. */\nexport interface OperationalInfo {\n /** How this deployment authenticates, in words. */\n auth: string;\n /** What decides who may edit, in the operator's own words. */\n admission?: string;\n dataDir?: string;\n sessionTtlHours?: number;\n sessionIdleHours?: number;\n /** Names of the secrets this process generated for itself. */\n generatedSecrets?: string[];\n}\n\n/** What /ready asks the composition. */\nexport interface RuntimeHealth {\n /**\n * Whether the site can be edited, with short reason codes. Never names the\n * repository: the endpoint is public.\n */\n ready?: () => { ok: boolean; reasons: string[] };\n}\n\nexport interface RuntimeContext {\n config: RuntimeConfig;\n site: SiteContext;\n logger: Logger;\n services: {\n content: ContentStore;\n media: MediaService;\n staging: ContentStagingService;\n };\n limiters: {\n api: RateLimiter;\n };\n operational(): OperationalInfo;\n health: RuntimeHealth;\n shutdown(): Promise<void>;\n}\n\nexport interface CreateRuntimeContextOptions {\n config: RuntimeConfig;\n model: Site;\n /** The working tree's files, without locks. The app builds it. */\n files: ContentFiles;\n /** Revision of the manifest the model was read from. Null when unknown. */\n schemaRevision?: string | null;\n siteId?: string;\n logger?: Logger;\n operational?: () => OperationalInfo;\n health?: RuntimeHealth;\n /** Who owns the working tree. Defaults to the developer. */\n policy?: OwnerPolicy;\n /** Shared with the owner's own work on the tree, such as a sync. */\n locks?: WorkspaceLocks;\n /** See `WorkingTreeStagingDeps.tree`. */\n tree?: GitTree;\n onShutdown?: () => void | Promise<void>;\n}\n\nexport function createRuntimeContext(options: CreateRuntimeContextOptions): RuntimeContext {\n const { config, model } = options;\n\n const logger =\n options.logger ??\n createLogger({\n level: config.LOG_LEVEL,\n pretty: !config.isProduction,\n });\n\n const schemaRevision = options.schemaRevision ?? null;\n\n // Services write through the lock wrapper; staging takes the locks itself.\n const locks = options.locks ?? createWorkspaceLocks();\n\n const site: SiteContext = {\n id: options.siteId ?? DEFAULT_SITE_ID,\n model,\n contentRepository: lockedFiles(options.files, locks.tree),\n schemaRevision,\n };\n\n const services = {\n content: createContentStore(model, site.contentRepository),\n media: createMediaService({\n files: site.contentRepository,\n maxBytes: config.MAX_UPLOAD_BYTES,\n }),\n staging: createWorkingTreeStagingService({\n model,\n files: options.files,\n contentRoot: config.contentRoot,\n schemaRevision,\n publishEnabled: config.MICA_LOCAL_PUBLISH,\n logger,\n locks,\n ...(options.policy ? { policy: options.policy } : {}),\n ...(options.tree ? { tree: options.tree } : {}),\n }),\n };\n\n const limiters = {\n // Generous: it stops runaway scripts, it does not shape traffic.\n api: createRateLimiter({ limit: 600, windowMs: 60_000 }),\n };\n\n return {\n config,\n site,\n logger,\n services,\n limiters,\n operational:\n options.operational ??\n (() => ({ auth: \"unspecified\" })),\n health: options.health ?? {},\n\n async shutdown() {\n limiters.api.stop();\n await options.onShutdown?.();\n },\n };\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Builds the runtime's services for one site, with no module-level singletons.\n * The app supplies the working tree's files and the owner policy, so the\n * runtime stays authentication-neutral.\n */\n\nimport type { Site } from \"@micacms/core\";\n\nimport type { RuntimeConfig } from \"./config.js\";\nimport { createLogger, type Logger } from \"./lib/logger.js\";\nimport { createRateLimiter, type RateLimiter } from \"./lib/rate-limit.js\";\nimport { readOnlyParts } from \"./schema.js\";\nimport { createMediaService, type MediaService } from \"./services/media.js\";\nimport type { ContentStagingService } from \"./services/changes.js\";\nimport { createContentStore, type ContentStore } from \"./services/content-store.js\";\nimport { createWorkingTreeStagingService } from \"./services/staging.js\";\nimport { createWorkspaceLocks, lockedFiles, type WorkspaceLocks } from \"./workspace/locks.js\";\nimport type { OwnerPolicy } from \"./workspace/policy.js\";\nimport type { ContentFiles } from \"./workspace/files.js\";\nimport type { GitTree } from \"./workspace/tree.js\";\n\n/** The one site this process serves. Several sites means several processes. */\nexport interface SiteContext {\n /** Identifies this site in logs. */\n id: string;\n /** The content model, read from the repository at startup. */\n model: Site;\n /** The working tree's files behind the workspace locks. Only Publish makes a commit. */\n contentRepository: ContentFiles;\n /**\n * Revision of `.mica/schema.json` when the model was loaded. The publish\n * drift gate compares against it.\n */\n schemaRevision: string | null;\n /** Collections and singletons this build cannot write, from `readOnlyParts`, with the reason. */\n readOnly: ReadonlyMap<string, string>;\n}\n\nexport const DEFAULT_SITE_ID = \"default\";\n\n/** Facts about the composition that the System page shows and the runtime cannot know. */\nexport interface OperationalInfo {\n /** How this deployment authenticates, in words. */\n auth: string;\n /** What decides who may edit, in the operator's own words. */\n admission?: string;\n dataDir?: string;\n sessionTtlHours?: number;\n sessionIdleHours?: number;\n /** Names of the secrets this process generated for itself. */\n generatedSecrets?: string[];\n}\n\n/** What /ready asks the composition. */\nexport interface RuntimeHealth {\n /**\n * Whether the site can be edited, with short reason codes. Never names the\n * repository: the endpoint is public.\n */\n ready?: () => { ok: boolean; reasons: string[] };\n}\n\nexport interface RuntimeContext {\n config: RuntimeConfig;\n site: SiteContext;\n logger: Logger;\n services: {\n content: ContentStore;\n media: MediaService;\n staging: ContentStagingService;\n };\n limiters: {\n api: RateLimiter;\n };\n operational(): OperationalInfo;\n health: RuntimeHealth;\n shutdown(): Promise<void>;\n}\n\nexport interface CreateRuntimeContextOptions {\n config: RuntimeConfig;\n model: Site;\n /** The working tree's files, without locks. The app builds it. */\n files: ContentFiles;\n /** Revision of the manifest the model was read from. Null when unknown. */\n schemaRevision?: string | null;\n siteId?: string;\n logger?: Logger;\n operational?: () => OperationalInfo;\n health?: RuntimeHealth;\n /** Who owns the working tree. Defaults to the developer. */\n policy?: OwnerPolicy;\n /** Shared with the owner's own work on the tree, such as a sync. */\n locks?: WorkspaceLocks;\n /** See `WorkingTreeStagingDeps.tree`. */\n tree?: GitTree;\n onShutdown?: () => void | Promise<void>;\n}\n\nexport function createRuntimeContext(options: CreateRuntimeContextOptions): RuntimeContext {\n const { config, model } = options;\n\n const logger =\n options.logger ??\n createLogger({\n level: config.MICA_LOG_LEVEL,\n pretty: !config.isProduction,\n });\n\n const schemaRevision = options.schemaRevision ?? null;\n\n // Services write through the lock wrapper; staging takes the locks itself.\n const locks = options.locks ?? createWorkspaceLocks();\n\n const readOnly = readOnlyParts(model);\n for (const reason of readOnly.values()) logger.warn(\"Read-only in this version of Mica\", { reason });\n\n const site: SiteContext = {\n id: options.siteId ?? DEFAULT_SITE_ID,\n model,\n contentRepository: lockedFiles(options.files, locks.tree),\n schemaRevision,\n readOnly,\n };\n\n const services = {\n content: createContentStore(model, site.contentRepository, readOnly),\n media: createMediaService({\n files: site.contentRepository,\n maxBytes: config.MICA_MAX_UPLOAD_BYTES,\n }),\n staging: createWorkingTreeStagingService({\n model,\n files: options.files,\n contentRoot: config.contentRoot,\n schemaRevision,\n readOnly,\n publishEnabled: config.MICA_LOCAL_PUBLISH,\n logger,\n locks,\n ...(options.policy ? { policy: options.policy } : {}),\n ...(options.tree ? { tree: options.tree } : {}),\n }),\n };\n\n const limiters = {\n // Generous: it stops runaway scripts, it does not shape traffic.\n api: createRateLimiter({ limit: 600, windowMs: 60_000 }),\n };\n\n return {\n config,\n site,\n logger,\n services,\n limiters,\n operational:\n options.operational ??\n (() => ({ auth: \"unspecified\" })),\n health: options.health ?? {},\n\n async shutdown() {\n limiters.api.stop();\n await options.onShutdown?.();\n },\n };\n}\n"]}
package/dist/http.d.ts CHANGED
@@ -15,7 +15,7 @@ export interface Env {
15
15
  };
16
16
  }
17
17
  /**
18
- * Client IP. Reads `X-Forwarded-For` only with TRUST_PROXY, or a client could
18
+ * Client IP. Reads `X-Forwarded-For` only with MICA_TRUST_PROXY, or a client could
19
19
  * spoof its address past the rate limit. The right-most entry is the address
20
20
  * the proxy saw; the entries to its left come from the client.
21
21
  */
@@ -29,6 +29,4 @@ export declare function actorOf(c: Context<Env>): {
29
29
  email: string;
30
30
  name: string;
31
31
  } | null;
32
- /** Parses a positive integer query parameter, falling back when absent. */
33
- export declare function intParam(value: string | undefined, fallback: number, max: number): number;
34
32
  //# sourceMappingURL=http.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,WAAW,GAAG;IAClB,SAAS,EAAE;QACT,GAAG,EAAE,cAAc,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB;;;WAGG;QACH,MAAM,EAAE,aAAa,CAAC;KACvB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAWhD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG3F;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIzF"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,WAAW,GAAG;IAClB,SAAS,EAAE;QACT,GAAG,EAAE,cAAc,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB;;;WAGG;QACH,MAAM,EAAE,aAAa,CAAC;KACvB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAWhD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG3F"}
package/dist/http.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Client IP. Reads `X-Forwarded-For` only with TRUST_PROXY, or a client could
2
+ * Client IP. Reads `X-Forwarded-For` only with MICA_TRUST_PROXY, or a client could
3
3
  * spoof its address past the rate limit. The right-most entry is the address
4
4
  * the proxy saw; the entries to its left come from the client.
5
5
  */
6
6
  export function clientIp(c) {
7
7
  const { config } = c.get("ctx");
8
- if (config.TRUST_PROXY) {
8
+ if (config.MICA_TRUST_PROXY) {
9
9
  const forwarded = c.req.header("x-forwarded-for");
10
10
  const last = forwarded?.split(",").at(-1)?.trim();
11
11
  if (last)
@@ -22,11 +22,4 @@ export function actorOf(c) {
22
22
  const identity = c.get("access")?.identity;
23
23
  return identity ? { id: identity.subject, email: identity.email, name: identity.name } : null;
24
24
  }
25
- /** Parses a positive integer query parameter, falling back when absent. */
26
- export function intParam(value, fallback, max) {
27
- const parsed = Number(value);
28
- if (!Number.isFinite(parsed) || parsed < 0)
29
- return fallback;
30
- return Math.min(Math.floor(parsed), max);
31
- }
32
25
  //# sourceMappingURL=http.js.map
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,CAAe;IACtC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEhC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QAClD,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,CAAC,GAAyE,CAAC;IACzF,OAAO,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,IAAI,SAAS,CAAC;AAC5D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,CAAe;IACrC,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC3C,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChG,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,KAAyB,EAAE,QAAgB,EAAE,GAAW;IAC/E,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC","sourcesContent":["import type { Context } from \"hono\";\n\nimport type { AccessContext } from \"@micacms/core\";\n\nimport type { RuntimeContext } from \"./context.js\";\nimport type { Logger } from \"./lib/logger.js\";\n\nexport interface Env {\n Variables: {\n ctx: RuntimeContext;\n logger: Logger;\n requestId: string;\n /**\n * Who this request acts as. The auth surface's gate sets it on every admin\n * route; the runtime never sets it.\n */\n access: AccessContext;\n };\n}\n\n/**\n * Client IP. Reads `X-Forwarded-For` only with TRUST_PROXY, or a client could\n * spoof its address past the rate limit. The right-most entry is the address\n * the proxy saw; the entries to its left come from the client.\n */\nexport function clientIp(c: Context<Env>): string {\n const { config } = c.get(\"ctx\");\n\n if (config.TRUST_PROXY) {\n const forwarded = c.req.header(\"x-forwarded-for\");\n const last = forwarded?.split(\",\").at(-1)?.trim();\n if (last) return last;\n }\n\n const info = c.env as { incoming?: { socket?: { remoteAddress?: string } } } | undefined;\n return info?.incoming?.socket?.remoteAddress ?? \"unknown\";\n}\n\n/**\n * The commit author. Null in the local editor, where the commit keeps the\n * developer's own Git identity.\n */\nexport function actorOf(c: Context<Env>): { id: string; email: string; name: string } | null {\n const identity = c.get(\"access\")?.identity;\n return identity ? { id: identity.subject, email: identity.email, name: identity.name } : null;\n}\n\n/** Parses a positive integer query parameter, falling back when absent. */\nexport function intParam(value: string | undefined, fallback: number, max: number): number {\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < 0) return fallback;\n return Math.min(Math.floor(parsed), max);\n}\n"]}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,CAAe;IACtC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEhC,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QAClD,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,CAAC,GAAyE,CAAC;IACzF,OAAO,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,IAAI,SAAS,CAAC;AAC5D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,CAAe;IACrC,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC3C,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChG,CAAC","sourcesContent":["import type { Context } from \"hono\";\n\nimport type { AccessContext } from \"@micacms/core\";\n\nimport type { RuntimeContext } from \"./context.js\";\nimport type { Logger } from \"./lib/logger.js\";\n\nexport interface Env {\n Variables: {\n ctx: RuntimeContext;\n logger: Logger;\n requestId: string;\n /**\n * Who this request acts as. The auth surface's gate sets it on every admin\n * route; the runtime never sets it.\n */\n access: AccessContext;\n };\n}\n\n/**\n * Client IP. Reads `X-Forwarded-For` only with MICA_TRUST_PROXY, or a client could\n * spoof its address past the rate limit. The right-most entry is the address\n * the proxy saw; the entries to its left come from the client.\n */\nexport function clientIp(c: Context<Env>): string {\n const { config } = c.get(\"ctx\");\n\n if (config.MICA_TRUST_PROXY) {\n const forwarded = c.req.header(\"x-forwarded-for\");\n const last = forwarded?.split(\",\").at(-1)?.trim();\n if (last) return last;\n }\n\n const info = c.env as { incoming?: { socket?: { remoteAddress?: string } } } | undefined;\n return info?.incoming?.socket?.remoteAddress ?? \"unknown\";\n}\n\n/**\n * The commit author. Null in the local editor, where the commit keeps the\n * developer's own Git identity.\n */\nexport function actorOf(c: Context<Env>): { id: string; email: string; name: string } | null {\n const identity = c.get(\"access\")?.identity;\n return identity ? { id: identity.subject, email: identity.email, name: identity.name } : null;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -6,11 +6,11 @@ export { createApp, allowAll, type AuthSurface, type CreateAppOptions } from "./
6
6
  export { createRuntimeContext, DEFAULT_SITE_ID, type CreateRuntimeContextOptions, type OperationalInfo, type RuntimeContext, type RuntimeHealth, type SiteContext, } from "./context.js";
7
7
  export { bool, ConfigError, deriveRuntime, describeConfigIssues, list, loadEnvFile, LOOPBACK, runtimeEnv, withoutBlanks, type RuntimeConfig, type RuntimeEnv, } from "./config.js";
8
8
  export { startServer, type StartServerOptions } from "./server.js";
9
- export { actorOf, clientIp, intParam, type Env } from "./http.js";
10
- export { checkSchema, loadSchema, SchemaError, schemaFingerprint, schemaOrigin } from "./schema.js";
9
+ export { actorOf, clientIp, type Env } from "./http.js";
10
+ export { checkSchema, layoutProblems, loadSchema, readOnlyParts, SchemaError, schemaFingerprint, schemaOrigin, } from "./schema.js";
11
11
  export { allowedOrigins, errorHandler, originGuard, rateLimit, requestContext, securityHeaders, } from "./middleware/index.js";
12
12
  export { adminAssetsExist } from "./routes/admin-spa.js";
13
- export { ApiError, badRequest, conflict, forbidden, notFound, unauthenticated, validationFailed, type ApiErrorBody, type ErrorCode, } from "./lib/errors.js";
13
+ export { ApiError, badRequest, conflict, forbidden, notFound, unauthenticated, type ApiErrorBody, type ErrorCode, } from "./lib/errors.js";
14
14
  export { createLogger, type Logger } from "./lib/logger.js";
15
15
  export { createRateLimiter, type RateLimiter } from "./lib/rate-limit.js";
16
16
  export { randomToken } from "./lib/id.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACxF,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,UAAU,EACV,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,GAAG,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,EAC/B,KAAK,sBAAsB,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACzG,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,QAAQ,EACR,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,EACX,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,UAAU,GACX,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACxF,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,MAAM,EACN,eAAe,EACf,OAAO,EACP,UAAU,EACV,WAAW,EACX,aAAa,EACb,KAAK,OAAO,EACZ,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -6,11 +6,11 @@ export { createApp, allowAll } from "./app.js";
6
6
  export { createRuntimeContext, DEFAULT_SITE_ID, } from "./context.js";
7
7
  export { bool, ConfigError, deriveRuntime, describeConfigIssues, list, loadEnvFile, LOOPBACK, runtimeEnv, withoutBlanks, } from "./config.js";
8
8
  export { startServer } from "./server.js";
9
- export { actorOf, clientIp, intParam } from "./http.js";
10
- export { checkSchema, loadSchema, SchemaError, schemaFingerprint, schemaOrigin } from "./schema.js";
9
+ export { actorOf, clientIp } from "./http.js";
10
+ export { checkSchema, layoutProblems, loadSchema, readOnlyParts, SchemaError, schemaFingerprint, schemaOrigin, } from "./schema.js";
11
11
  export { allowedOrigins, errorHandler, originGuard, rateLimit, requestContext, securityHeaders, } from "./middleware/index.js";
12
12
  export { adminAssetsExist } from "./routes/admin-spa.js";
13
- export { ApiError, badRequest, conflict, forbidden, notFound, unauthenticated, validationFailed, } from "./lib/errors.js";
13
+ export { ApiError, badRequest, conflict, forbidden, notFound, unauthenticated, } from "./lib/errors.js";
14
14
  export { createLogger } from "./lib/logger.js";
15
15
  export { createRateLimiter } from "./lib/rate-limit.js";
16
16
  export { randomToken } from "./lib/id.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAA2C,MAAM,UAAU,CAAC;AACxF,OAAO,EACL,oBAAoB,EACpB,eAAe,GAMhB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,UAAU,EACV,aAAa,GAGd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAA2B,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAY,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,eAAe,EACf,gBAAgB,GAGjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,YAAY,EAAe,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAoB,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAShB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,GAEhC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAqC,MAAM,qBAAqB,CAAC;AACzG,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,eAAe,GAIhB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,UAAU,GAMX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,QAAQ,EACR,SAAS,GAIV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,GAIZ,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,EAAE,eAAe,EAA+B,MAAM,0BAA0B,CAAC;AACxF,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,MAAM,EACN,eAAe,EACf,OAAO,EACP,UAAU,EACV,WAAW,EACX,aAAa,GAGd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["/**\n * @micacms/runtime: the single-site, authentication-neutral Mica runtime.\n * It must never import an auth implementation; the composing app supplies the `AuthSurface`.\n */\n\nexport { createApp, allowAll, type AuthSurface, type CreateAppOptions } from \"./app.js\";\nexport {\n createRuntimeContext,\n DEFAULT_SITE_ID,\n type CreateRuntimeContextOptions,\n type OperationalInfo,\n type RuntimeContext,\n type RuntimeHealth,\n type SiteContext,\n} from \"./context.js\";\nexport {\n bool,\n ConfigError,\n deriveRuntime,\n describeConfigIssues,\n list,\n loadEnvFile,\n LOOPBACK,\n runtimeEnv,\n withoutBlanks,\n type RuntimeConfig,\n type RuntimeEnv,\n} from \"./config.js\";\nexport { startServer, type StartServerOptions } from \"./server.js\";\nexport { actorOf, clientIp, intParam, type Env } from \"./http.js\";\nexport { checkSchema, loadSchema, SchemaError, schemaFingerprint, schemaOrigin } from \"./schema.js\";\nexport {\n allowedOrigins,\n errorHandler,\n originGuard,\n rateLimit,\n requestContext,\n securityHeaders,\n} from \"./middleware/index.js\";\nexport { adminAssetsExist } from \"./routes/admin-spa.js\";\nexport {\n ApiError,\n badRequest,\n conflict,\n forbidden,\n notFound,\n unauthenticated,\n validationFailed,\n type ApiErrorBody,\n type ErrorCode,\n} from \"./lib/errors.js\";\nexport { createLogger, type Logger } from \"./lib/logger.js\";\nexport { createRateLimiter, type RateLimiter } from \"./lib/rate-limit.js\";\nexport { randomToken } from \"./lib/id.js\";\nexport { DEFAULT_BASE_URL, DEFAULT_PORT } from \"./lib/ports.js\";\nexport { VERSION } from \"./lib/version.js\";\nexport {\n analyseChanges,\n classifyPath,\n describeProblems,\n flattenProblems,\n type AnnotatedChange,\n type ChangeOwner,\n type ContentStagingService,\n type RepositoryInfo,\n type PublishInput,\n type StagingProblem,\n type StagingStatus,\n type StagingValidation,\n} from \"./services/changes.js\";\nexport {\n createWorkingTreeStagingService,\n type WorkingTreeStagingDeps,\n} from \"./services/staging.js\";\nexport { createMediaService, formatBytes, type MediaFile, type MediaService } from \"./services/media.js\";\nexport {\n createContentStore,\n ContentError,\n ValidationError,\n type ContentStore,\n type SavedEntry,\n type SaveEntryInput,\n} from \"./services/content-store.js\";\nexport {\n checkChangePaths,\n ConflictError,\n contentFiles,\n FilesError,\n type Author,\n type ContentFiles,\n type ContentFilesOptions,\n type FileChange,\n type FileChanges,\n} from \"./workspace/files.js\";\nexport {\n developerEnvironment,\n firstLine,\n GitError,\n gitRunner,\n type GitCallOptions,\n type GitRunner,\n type GitRunnerOptions,\n} from \"./workspace/git.js\";\nexport {\n createMutex,\n createReadWriteLock,\n createWorkspaceLocks,\n lockedFiles,\n type Mutex,\n type ReadWriteLock,\n type WorkspaceLocks,\n} from \"./workspace/locks.js\";\nexport type {\n OwnerPolicy,\n PublishAvailability,\n RepositoryDescription,\n SyncConflict,\n SyncStatus,\n} from \"./workspace/policy.js\";\nexport { developerPolicy, type DeveloperPolicyOptions } from \"./workspace/developer.js\";\nexport {\n commitPaths,\n headRevision,\n headSource,\n inRepo,\n listTreeChanges,\n LITERAL,\n locateTree,\n parseStatus,\n recommitPaths,\n type GitTree,\n type TreeChange,\n} from \"./workspace/tree.js\";\nexport { schemaStats } from \"./lib/schema-stats.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAA2C,MAAM,UAAU,CAAC;AACxF,OAAO,EACL,oBAAoB,EACpB,eAAe,GAMhB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,UAAU,EACV,aAAa,GAGd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAA2B,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAY,MAAM,WAAW,CAAC;AACxD,OAAO,EACL,WAAW,EACX,cAAc,EACd,UAAU,EACV,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,eAAe,GAGhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,YAAY,EAAe,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAoB,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAShB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,GAEhC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAqC,MAAM,qBAAqB,CAAC;AACzG,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,eAAe,GAIhB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,UAAU,GAMX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,QAAQ,EACR,SAAS,GAIV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,GAIZ,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,EAAE,eAAe,EAA+B,MAAM,0BAA0B,CAAC;AACxF,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,MAAM,EACN,eAAe,EACf,OAAO,EACP,UAAU,EACV,WAAW,EACX,aAAa,GAGd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["/**\n * @micacms/runtime: the single-site, authentication-neutral Mica runtime.\n * It must never import an auth implementation; the composing app supplies the `AuthSurface`.\n */\n\nexport { createApp, allowAll, type AuthSurface, type CreateAppOptions } from \"./app.js\";\nexport {\n createRuntimeContext,\n DEFAULT_SITE_ID,\n type CreateRuntimeContextOptions,\n type OperationalInfo,\n type RuntimeContext,\n type RuntimeHealth,\n type SiteContext,\n} from \"./context.js\";\nexport {\n bool,\n ConfigError,\n deriveRuntime,\n describeConfigIssues,\n list,\n loadEnvFile,\n LOOPBACK,\n runtimeEnv,\n withoutBlanks,\n type RuntimeConfig,\n type RuntimeEnv,\n} from \"./config.js\";\nexport { startServer, type StartServerOptions } from \"./server.js\";\nexport { actorOf, clientIp, type Env } from \"./http.js\";\nexport {\n checkSchema,\n layoutProblems,\n loadSchema,\n readOnlyParts,\n SchemaError,\n schemaFingerprint,\n schemaOrigin,\n} from \"./schema.js\";\nexport {\n allowedOrigins,\n errorHandler,\n originGuard,\n rateLimit,\n requestContext,\n securityHeaders,\n} from \"./middleware/index.js\";\nexport { adminAssetsExist } from \"./routes/admin-spa.js\";\nexport {\n ApiError,\n badRequest,\n conflict,\n forbidden,\n notFound,\n unauthenticated,\n type ApiErrorBody,\n type ErrorCode,\n} from \"./lib/errors.js\";\nexport { createLogger, type Logger } from \"./lib/logger.js\";\nexport { createRateLimiter, type RateLimiter } from \"./lib/rate-limit.js\";\nexport { randomToken } from \"./lib/id.js\";\nexport { DEFAULT_BASE_URL, DEFAULT_PORT } from \"./lib/ports.js\";\nexport { VERSION } from \"./lib/version.js\";\nexport {\n analyseChanges,\n classifyPath,\n describeProblems,\n flattenProblems,\n type AnnotatedChange,\n type ChangeOwner,\n type ContentStagingService,\n type RepositoryInfo,\n type PublishInput,\n type StagingProblem,\n type StagingStatus,\n type StagingValidation,\n} from \"./services/changes.js\";\nexport {\n createWorkingTreeStagingService,\n type WorkingTreeStagingDeps,\n} from \"./services/staging.js\";\nexport { createMediaService, formatBytes, type MediaFile, type MediaService } from \"./services/media.js\";\nexport {\n createContentStore,\n ContentError,\n ValidationError,\n type ContentStore,\n type SavedEntry,\n type SaveEntryInput,\n} from \"./services/content-store.js\";\nexport {\n checkChangePaths,\n ConflictError,\n contentFiles,\n FilesError,\n type Author,\n type ContentFiles,\n type ContentFilesOptions,\n type FileChange,\n type FileChanges,\n} from \"./workspace/files.js\";\nexport {\n developerEnvironment,\n firstLine,\n GitError,\n gitRunner,\n type GitCallOptions,\n type GitRunner,\n type GitRunnerOptions,\n} from \"./workspace/git.js\";\nexport {\n createMutex,\n createReadWriteLock,\n createWorkspaceLocks,\n lockedFiles,\n type Mutex,\n type ReadWriteLock,\n type WorkspaceLocks,\n} from \"./workspace/locks.js\";\nexport type {\n OwnerPolicy,\n PublishAvailability,\n RepositoryDescription,\n SyncConflict,\n SyncStatus,\n} from \"./workspace/policy.js\";\nexport { developerPolicy, type DeveloperPolicyOptions } from \"./workspace/developer.js\";\nexport {\n commitPaths,\n headRevision,\n headSource,\n inRepo,\n listTreeChanges,\n LITERAL,\n locateTree,\n parseStatus,\n recommitPaths,\n type GitTree,\n type TreeChange,\n} from \"./workspace/tree.js\";\nexport { schemaStats } from \"./lib/schema-stats.js\";\n"]}
@@ -6,7 +6,7 @@
6
6
  * treated as a bug: logged with its stack, reported as a bare 500.
7
7
  */
8
8
  import type { FieldIssue } from "@micacms/core";
9
- export type ErrorCode = "bad_request" | "validation_failed" | "unauthenticated" | "invalid_credentials" | "forbidden" | "not_found" | "conflict" | "payload_too_large" | "unsupported_media_type" | "rate_limited" | "internal_error"
9
+ export type ErrorCode = "bad_request" | "validation_failed" | "unauthenticated" | "forbidden" | "not_found" | "conflict" | "payload_too_large" | "unsupported_media_type" | "rate_limited" | "internal_error"
10
10
  /** An identity provider Mica depends on could not be reached. */
11
11
  | "service_unavailable";
12
12
  export interface ApiErrorBody {
@@ -35,5 +35,4 @@ export declare const unauthenticated: (message?: string) => ApiError;
35
35
  export declare const forbidden: (message?: string) => ApiError;
36
36
  export declare const notFound: (message?: string) => ApiError;
37
37
  export declare const conflict: (message: string) => ApiError;
38
- export declare const validationFailed: (issues: FieldIssue[], message?: string) => ApiError;
39
38
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,SAAS,GACjB,aAAa,GACb,mBAAmB,GACnB,iBAAiB,GACjB,qBAAqB,GACrB,WAAW,GACX,WAAW,GACX,UAAU,GACV,mBAAmB,GACnB,wBAAwB,GACxB,cAAc,GACd,gBAAgB;AAClB,iEAAiE;GAC/D,qBAAqB,CAAC;AAiB1B,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE;QACL,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;KACvB,CAAC;CACH;AAED,qBAAa,QAAS,SAAQ,KAAK;IACxB,IAAI,SAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAGxC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;IAS5F,MAAM,IAAI,YAAY;CASvB;AAED,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,aAAyC,CAAC;AACpF,eAAO,MAAM,eAAe,GAAI,gBAAoC,aAC1B,CAAC;AAC3C,eAAO,MAAM,SAAS,GAAI,gBAAoD,aAC1C,CAAC;AACrC,eAAO,MAAM,QAAQ,GAAI,gBAAsB,aAAuC,CAAC;AACvF,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,aAAsC,CAAC;AAC/E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,UAAU,EAAE,EAAE,gBAAqC,aACpC,CAAC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,SAAS,GACjB,aAAa,GACb,mBAAmB,GACnB,iBAAiB,GACjB,WAAW,GACX,WAAW,GACX,UAAU,GACV,mBAAmB,GACnB,wBAAwB,GACxB,cAAc,GACd,gBAAgB;AAClB,iEAAiE;GAC/D,qBAAqB,CAAC;AAgB1B,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE;QACL,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;KACvB,CAAC;CACH;AAED,qBAAa,QAAS,SAAQ,KAAK;IACxB,IAAI,SAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAGxC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;IAS5F,MAAM,IAAI,YAAY;CASvB;AAED,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,aAAyC,CAAC;AACpF,eAAO,MAAM,eAAe,GAAI,gBAAoC,aAC1B,CAAC;AAC3C,eAAO,MAAM,SAAS,GAAI,gBAAoD,aAC1C,CAAC;AACrC,eAAO,MAAM,QAAQ,GAAI,gBAAsB,aAAuC,CAAC;AACvF,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,aAAsC,CAAC"}
@@ -9,7 +9,6 @@ const STATUS = {
9
9
  bad_request: 400,
10
10
  validation_failed: 422,
11
11
  unauthenticated: 401,
12
- invalid_credentials: 401,
13
12
  forbidden: 403,
14
13
  not_found: 404,
15
14
  conflict: 409,
@@ -48,5 +47,4 @@ export const unauthenticated = (message = "Authentication required.") => new Api
48
47
  export const forbidden = (message = "You do not have access to this resource.") => new ApiError("forbidden", message);
49
48
  export const notFound = (message = "Not found.") => new ApiError("not_found", message);
50
49
  export const conflict = (message) => new ApiError("conflict", message);
51
- export const validationFailed = (issues, message = "The content is not valid.") => new ApiError("validation_failed", message, { issues });
52
50
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAmBH,MAAM,MAAM,GAA8B;IACxC,WAAW,EAAE,GAAG;IAChB,iBAAiB,EAAE,GAAG;IACtB,eAAe,EAAE,GAAG;IACpB,mBAAmB,EAAE,GAAG;IACxB,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,GAAG;IACb,iBAAiB,EAAE,GAAG;IACtB,sBAAsB,EAAE,GAAG;IAC3B,YAAY,EAAE,GAAG;IACjB,cAAc,EAAE,GAAG;IACnB,mBAAmB,EAAE,GAAG;CACzB,CAAC;AAUF,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,GAAG,UAAU,CAAC;IAClB,IAAI,CAAY;IAChB,MAAM,CAAS;IACf,MAAM,CAAgB;IAC/B,kEAAkE;IACzD,OAAO,CAA0B;IAE1C,YACE,IAAe,EACf,OAAe,EACf,UAAwF,EAAE;QAE1F,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;IAED,MAAM;QACJ,OAAO;YACL,KAAK,EAAE;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChD;SACF,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AACpF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,GAAG,0BAA0B,EAAE,EAAE,CACtE,IAAI,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAO,GAAG,0CAA0C,EAAE,EAAE,CAChF,IAAI,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,OAAO,GAAG,YAAY,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AACvF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAoB,EAAE,OAAO,GAAG,2BAA2B,EAAE,EAAE,CAC9F,IAAI,QAAQ,CAAC,mBAAmB,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC","sourcesContent":["/**\n * Error taxonomy.\n *\n * Every failure the API reports on purpose is an `ApiError` carrying a stable\n * machine-readable `code`. Anything else that reaches the error middleware is\n * treated as a bug: logged with its stack, reported as a bare 500.\n */\n\nimport type { FieldIssue } from \"@micacms/core\";\n\nexport type ErrorCode =\n | \"bad_request\"\n | \"validation_failed\"\n | \"unauthenticated\"\n | \"invalid_credentials\"\n | \"forbidden\"\n | \"not_found\"\n | \"conflict\"\n | \"payload_too_large\"\n | \"unsupported_media_type\"\n | \"rate_limited\"\n | \"internal_error\"\n /** An identity provider Mica depends on could not be reached. */\n | \"service_unavailable\";\n\nconst STATUS: Record<ErrorCode, number> = {\n bad_request: 400,\n validation_failed: 422,\n unauthenticated: 401,\n invalid_credentials: 401,\n forbidden: 403,\n not_found: 404,\n conflict: 409,\n payload_too_large: 413,\n unsupported_media_type: 415,\n rate_limited: 429,\n internal_error: 500,\n service_unavailable: 503,\n};\n\nexport interface ApiErrorBody {\n error: {\n code: ErrorCode;\n message: string;\n issues?: FieldIssue[];\n };\n}\n\nexport class ApiError extends Error {\n override name = \"ApiError\";\n readonly code: ErrorCode;\n readonly status: number;\n readonly issues?: FieldIssue[];\n /** Extra response headers, e.g. `Retry-After` on a rate limit. */\n readonly headers?: Record<string, string>;\n\n constructor(\n code: ErrorCode,\n message: string,\n options: { issues?: FieldIssue[]; headers?: Record<string, string>; cause?: unknown } = {},\n ) {\n super(message, { cause: options.cause });\n this.code = code;\n this.status = STATUS[code];\n this.issues = options.issues;\n this.headers = options.headers;\n }\n\n toBody(): ApiErrorBody {\n return {\n error: {\n code: this.code,\n message: this.message,\n ...(this.issues ? { issues: this.issues } : {}),\n },\n };\n }\n}\n\nexport const badRequest = (message: string) => new ApiError(\"bad_request\", message);\nexport const unauthenticated = (message = \"Authentication required.\") =>\n new ApiError(\"unauthenticated\", message);\nexport const forbidden = (message = \"You do not have access to this resource.\") =>\n new ApiError(\"forbidden\", message);\nexport const notFound = (message = \"Not found.\") => new ApiError(\"not_found\", message);\nexport const conflict = (message: string) => new ApiError(\"conflict\", message);\nexport const validationFailed = (issues: FieldIssue[], message = \"The content is not valid.\") =>\n new ApiError(\"validation_failed\", message, { issues });\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkBH,MAAM,MAAM,GAA8B;IACxC,WAAW,EAAE,GAAG;IAChB,iBAAiB,EAAE,GAAG;IACtB,eAAe,EAAE,GAAG;IACpB,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,GAAG;IACb,iBAAiB,EAAE,GAAG;IACtB,sBAAsB,EAAE,GAAG;IAC3B,YAAY,EAAE,GAAG;IACjB,cAAc,EAAE,GAAG;IACnB,mBAAmB,EAAE,GAAG;CACzB,CAAC;AAUF,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,GAAG,UAAU,CAAC;IAClB,IAAI,CAAY;IAChB,MAAM,CAAS;IACf,MAAM,CAAgB;IAC/B,kEAAkE;IACzD,OAAO,CAA0B;IAE1C,YACE,IAAe,EACf,OAAe,EACf,UAAwF,EAAE;QAE1F,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;IAED,MAAM;QACJ,OAAO;YACL,KAAK,EAAE;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChD;SACF,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AACpF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,GAAG,0BAA0B,EAAE,EAAE,CACtE,IAAI,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAO,GAAG,0CAA0C,EAAE,EAAE,CAChF,IAAI,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,OAAO,GAAG,YAAY,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AACvF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC","sourcesContent":["/**\n * Error taxonomy.\n *\n * Every failure the API reports on purpose is an `ApiError` carrying a stable\n * machine-readable `code`. Anything else that reaches the error middleware is\n * treated as a bug: logged with its stack, reported as a bare 500.\n */\n\nimport type { FieldIssue } from \"@micacms/core\";\n\nexport type ErrorCode =\n | \"bad_request\"\n | \"validation_failed\"\n | \"unauthenticated\"\n | \"forbidden\"\n | \"not_found\"\n | \"conflict\"\n | \"payload_too_large\"\n | \"unsupported_media_type\"\n | \"rate_limited\"\n | \"internal_error\"\n /** An identity provider Mica depends on could not be reached. */\n | \"service_unavailable\";\n\nconst STATUS: Record<ErrorCode, number> = {\n bad_request: 400,\n validation_failed: 422,\n unauthenticated: 401,\n forbidden: 403,\n not_found: 404,\n conflict: 409,\n payload_too_large: 413,\n unsupported_media_type: 415,\n rate_limited: 429,\n internal_error: 500,\n service_unavailable: 503,\n};\n\nexport interface ApiErrorBody {\n error: {\n code: ErrorCode;\n message: string;\n issues?: FieldIssue[];\n };\n}\n\nexport class ApiError extends Error {\n override name = \"ApiError\";\n readonly code: ErrorCode;\n readonly status: number;\n readonly issues?: FieldIssue[];\n /** Extra response headers, e.g. `Retry-After` on a rate limit. */\n readonly headers?: Record<string, string>;\n\n constructor(\n code: ErrorCode,\n message: string,\n options: { issues?: FieldIssue[]; headers?: Record<string, string>; cause?: unknown } = {},\n ) {\n super(message, { cause: options.cause });\n this.code = code;\n this.status = STATUS[code];\n this.issues = options.issues;\n this.headers = options.headers;\n }\n\n toBody(): ApiErrorBody {\n return {\n error: {\n code: this.code,\n message: this.message,\n ...(this.issues ? { issues: this.issues } : {}),\n },\n };\n }\n}\n\nexport const badRequest = (message: string) => new ApiError(\"bad_request\", message);\nexport const unauthenticated = (message = \"Authentication required.\") =>\n new ApiError(\"unauthenticated\", message);\nexport const forbidden = (message = \"You do not have access to this resource.\") =>\n new ApiError(\"forbidden\", message);\nexport const notFound = (message = \"Not found.\") => new ApiError(\"not_found\", message);\nexport const conflict = (message: string) => new ApiError(\"conflict\", message);\n"]}
package/dist/lib/id.d.ts CHANGED
@@ -1,13 +1,3 @@
1
- /**
2
- * ULID generation.
3
- *
4
- * Lexicographically sortable, so `ORDER BY id` matches creation order and
5
- * pagination stays stable without a secondary sort. This is an identifier
6
- * encoding, not a cryptographic construction — the randomness comes from
7
- * `crypto.randomBytes`.
8
- */
9
- export declare function ulid(now?: number): string;
10
- export declare function isUlid(value: string): boolean;
11
1
  /** URL-safe random token for sessions and one-time links. */
12
2
  export declare function randomToken(bytes?: number): string;
13
3
  //# sourceMappingURL=id.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/lib/id.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAsCH,wBAAgB,IAAI,CAAC,GAAG,SAAa,GAAG,MAAM,CAQ7C;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,KAAK,SAAK,GAAG,MAAM,CAE9C"}
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/lib/id.ts"],"names":[],"mappings":"AAEA,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,KAAK,SAAK,GAAG,MAAM,CAE9C"}