@micacms/runtime 0.4.1 → 0.5.0-rc.2

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 (112) hide show
  1. package/README.md +2 -2
  2. package/dist/app.d.ts +8 -37
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js +7 -42
  5. package/dist/app.js.map +1 -1
  6. package/dist/config.d.ts +12 -82
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +14 -82
  9. package/dist/config.js.map +1 -1
  10. package/dist/context.d.ts +37 -66
  11. package/dist/context.d.ts.map +1 -1
  12. package/dist/context.js +24 -75
  13. package/dist/context.js.map +1 -1
  14. package/dist/http.d.ts +8 -16
  15. package/dist/http.d.ts.map +1 -1
  16. package/dist/http.js +6 -13
  17. package/dist/http.js.map +1 -1
  18. package/dist/index.d.ts +13 -14
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +12 -14
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/errors.js.map +1 -1
  23. package/dist/lib/file-type.js.map +1 -1
  24. package/dist/lib/id.js.map +1 -1
  25. package/dist/lib/logger.js.map +1 -1
  26. package/dist/lib/ports.js.map +1 -1
  27. package/dist/lib/rate-limit.js.map +1 -1
  28. package/dist/lib/schema-stats.js.map +1 -1
  29. package/dist/lib/version.js.map +1 -1
  30. package/dist/middleware/index.js.map +1 -1
  31. package/dist/routes/admin-content.d.ts +4 -17
  32. package/dist/routes/admin-content.d.ts.map +1 -1
  33. package/dist/routes/admin-content.js +11 -51
  34. package/dist/routes/admin-content.js.map +1 -1
  35. package/dist/routes/admin-media.d.ts +3 -14
  36. package/dist/routes/admin-media.d.ts.map +1 -1
  37. package/dist/routes/admin-media.js +7 -29
  38. package/dist/routes/admin-media.js.map +1 -1
  39. package/dist/routes/admin-spa.js.map +1 -1
  40. package/dist/routes/admin-staging.d.ts +4 -9
  41. package/dist/routes/admin-staging.d.ts.map +1 -1
  42. package/dist/routes/admin-staging.js +31 -18
  43. package/dist/routes/admin-staging.js.map +1 -1
  44. package/dist/routes/admin-system.d.ts +2 -8
  45. package/dist/routes/admin-system.d.ts.map +1 -1
  46. package/dist/routes/admin-system.js +9 -44
  47. package/dist/routes/admin-system.js.map +1 -1
  48. package/dist/routes/health.d.ts +3 -10
  49. package/dist/routes/health.d.ts.map +1 -1
  50. package/dist/routes/health.js +9 -26
  51. package/dist/routes/health.js.map +1 -1
  52. package/dist/schema.d.ts +10 -38
  53. package/dist/schema.d.ts.map +1 -1
  54. package/dist/schema.js +14 -65
  55. package/dist/schema.js.map +1 -1
  56. package/dist/server.d.ts +2 -10
  57. package/dist/server.d.ts.map +1 -1
  58. package/dist/server.js +3 -9
  59. package/dist/server.js.map +1 -1
  60. package/dist/services/changes.d.ts +150 -0
  61. package/dist/services/changes.d.ts.map +1 -0
  62. package/dist/services/changes.js +339 -0
  63. package/dist/services/changes.js.map +1 -0
  64. package/dist/services/content-store.d.ts +61 -0
  65. package/dist/services/content-store.d.ts.map +1 -0
  66. package/dist/services/content-store.js +330 -0
  67. package/dist/services/content-store.js.map +1 -0
  68. package/dist/services/media.d.ts +9 -29
  69. package/dist/services/media.d.ts.map +1 -1
  70. package/dist/services/media.js +17 -57
  71. package/dist/services/media.js.map +1 -1
  72. package/dist/services/staging.d.ts +35 -167
  73. package/dist/services/staging.d.ts.map +1 -1
  74. package/dist/services/staging.js +195 -458
  75. package/dist/services/staging.js.map +1 -1
  76. package/dist/workspace/developer.d.ts +14 -0
  77. package/dist/workspace/developer.d.ts.map +1 -0
  78. package/dist/workspace/developer.js +31 -0
  79. package/dist/workspace/developer.js.map +1 -0
  80. package/dist/workspace/files.d.ts +70 -0
  81. package/dist/workspace/files.d.ts.map +1 -0
  82. package/dist/workspace/files.js +176 -0
  83. package/dist/workspace/files.js.map +1 -0
  84. package/dist/workspace/git.d.ts +45 -0
  85. package/dist/workspace/git.d.ts.map +1 -0
  86. package/dist/workspace/git.js +68 -0
  87. package/dist/workspace/git.js.map +1 -0
  88. package/dist/workspace/locks.d.ts +33 -0
  89. package/dist/workspace/locks.d.ts.map +1 -0
  90. package/dist/workspace/locks.js +76 -0
  91. package/dist/workspace/locks.js.map +1 -0
  92. package/dist/workspace/policy.d.ts +92 -0
  93. package/dist/workspace/policy.d.ts.map +1 -0
  94. package/dist/workspace/policy.js +7 -0
  95. package/dist/workspace/policy.js.map +1 -0
  96. package/dist/workspace/tree.d.ts +73 -0
  97. package/dist/workspace/tree.d.ts.map +1 -0
  98. package/dist/workspace/tree.js +229 -0
  99. package/dist/workspace/tree.js.map +1 -0
  100. package/package.json +2 -3
  101. package/dist/db/driver.d.ts +0 -35
  102. package/dist/db/driver.d.ts.map +0 -1
  103. package/dist/db/driver.js +0 -95
  104. package/dist/db/driver.js.map +0 -1
  105. package/dist/lib/git.d.ts +0 -29
  106. package/dist/lib/git.d.ts.map +0 -1
  107. package/dist/lib/git.js +0 -62
  108. package/dist/lib/git.js.map +0 -1
  109. package/dist/services/staging-local.d.ts +0 -58
  110. package/dist/services/staging-local.d.ts.map +0 -1
  111. package/dist/services/staging-local.js +0 -391
  112. package/dist/services/staging-local.js.map +0 -1
package/dist/context.d.ts CHANGED
@@ -1,62 +1,38 @@
1
1
  /**
2
- * The runtime's composition: one site, its services, and nothing about who
3
- * is allowed in.
4
- *
5
- * Everything is constructed here and passed down explicitly — no module-level
6
- * singletons, no service locator. The composing app builds the connector and,
7
- * where one exists, the draft store and the database; the runtime receives
8
- * them and never asks where they came from. That is what keeps this package
9
- * authentication-neutral and database-free on a developer's machine: the
10
- * local editor simply never hands it either.
2
+ * Builds the runtime's services for one site, with no module-level singletons.
3
+ * The app supplies the working tree's files and the owner policy, so the
4
+ * runtime stays authentication-neutral.
11
5
  */
12
- import { type ContentStore } from "@micacms/connectors";
13
- import type { DraftStore, RepositoryConnector, Site } from "@micacms/core";
6
+ import type { Site } from "@micacms/core";
14
7
  import type { RuntimeConfig } from "./config.js";
15
8
  import { type Logger } from "./lib/logger.js";
16
9
  import { type RateLimiter } from "./lib/rate-limit.js";
17
10
  import { type MediaService } from "./services/media.js";
18
- import { type ContentStagingService } from "./services/staging.js";
19
- /**
20
- * The site this process serves. One, explicitly.
21
- *
22
- * Explicit rather than implicit even though there is only ever one: a runtime
23
- * that names its site is testable and has no global state, and a runtime that
24
- * *assumes* its site is the one that quietly grows a second. Several sites
25
- * means several processes.
26
- */
11
+ import type { ContentStagingService } from "./services/changes.js";
12
+ import { type ContentStore } from "./services/content-store.js";
13
+ import { type WorkspaceLocks } from "./workspace/locks.js";
14
+ import type { OwnerPolicy } from "./workspace/policy.js";
15
+ import type { ContentFiles } from "./workspace/files.js";
16
+ import type { GitTree } from "./workspace/tree.js";
17
+ /** The one site this process serves. Several sites means several processes. */
27
18
  export interface SiteContext {
28
- /** Identifies this site in logs. Constant while one runtime serves one site. */
19
+ /** Identifies this site in logs. */
29
20
  id: string;
30
21
  /** The content model, read from the repository at startup. */
31
22
  model: Site;
32
- /**
33
- * What everything reads and saves through. With a draft store this is the
34
- * staged overlay — saves land in the draft, and only the staging service's
35
- * Publish reaches the repository itself.
36
- */
37
- contentRepository: RepositoryConnector;
23
+ /** The working tree's files behind the workspace locks. Only Publish makes a commit. */
24
+ contentRepository: ContentFiles;
38
25
  /**
39
26
  * Revision of `.mica/schema.json` when the model was loaded. The publish
40
- * drift gate compares against it — see decisions.md.
27
+ * drift gate compares against it.
41
28
  */
42
29
  schemaRevision: string | null;
43
30
  }
44
31
  export declare const DEFAULT_SITE_ID = "default";
45
- /**
46
- * What the composing app wants the System page to say about itself — the
47
- * facts the runtime cannot know because they belong to the composition:
48
- * whether there is a database, how this deployment authenticates, and which
49
- * rule admits people.
50
- */
32
+ /** Facts about the composition that the System page shows and the runtime cannot know. */
51
33
  export interface OperationalInfo {
52
34
  /** How this deployment authenticates, in words. */
53
35
  auth: string;
54
- /** Null where there is no database, which is the local editor's answer. */
55
- database: {
56
- path: string;
57
- version: number;
58
- bytes: number | null;
59
- } | null;
60
36
  /** What decides who may edit, in the operator's own words. */
61
37
  admission?: string;
62
38
  dataDir?: string;
@@ -65,6 +41,17 @@ export interface OperationalInfo {
65
41
  /** Names of the secrets this process generated for itself. */
66
42
  generatedSecrets?: string[];
67
43
  }
44
+ /** What /ready asks the composition. */
45
+ export interface RuntimeHealth {
46
+ /**
47
+ * Whether the site can be edited, with short reason codes. Never names the
48
+ * repository: the endpoint is public.
49
+ */
50
+ ready?: () => {
51
+ ok: boolean;
52
+ reasons: string[];
53
+ };
54
+ }
68
55
  export interface RuntimeContext {
69
56
  config: RuntimeConfig;
70
57
  site: SiteContext;
@@ -77,44 +64,28 @@ export interface RuntimeContext {
77
64
  limiters: {
78
65
  api: RateLimiter;
79
66
  };
80
- /** App-supplied: what the System page says about the composition. */
81
67
  operational(): OperationalInfo;
82
- /** App-supplied active probes for /ready. Absent means nothing to probe. */
83
- health: {
84
- database?: () => void;
85
- };
86
- /** Releases timers, and whatever the app added on top. */
68
+ health: RuntimeHealth;
87
69
  shutdown(): Promise<void>;
88
70
  }
89
71
  export interface CreateRuntimeContextOptions {
90
72
  config: RuntimeConfig;
91
73
  model: Site;
92
- /** How the content repository is reached. The app builds it. */
93
- connector: RepositoryConnector;
94
- /**
95
- * Where saves accumulate when the repository is remote. Absent means the
96
- * working tree is the draft workspace — the local editor, or a self-hosted
97
- * deployment with a checkout beside it.
98
- */
99
- drafts?: DraftStore;
74
+ /** The working tree's files, without locks. The app builds it. */
75
+ files: ContentFiles;
100
76
  /** Revision of the manifest the model was read from. Null when unknown. */
101
77
  schemaRevision?: string | null;
102
78
  siteId?: string;
103
79
  logger?: Logger;
104
80
  operational?: () => OperationalInfo;
105
- health?: {
106
- database?: () => void;
107
- };
81
+ health?: RuntimeHealth;
82
+ /** Who owns the working tree. Defaults to the developer. */
83
+ policy?: OwnerPolicy;
84
+ /** Shared with the owner's own work on the tree, such as a sync. */
85
+ locks?: WorkspaceLocks;
86
+ /** See `WorkingTreeStagingDeps.tree`. */
87
+ tree?: GitTree;
108
88
  onShutdown?: () => void | Promise<void>;
109
89
  }
110
90
  export declare function createRuntimeContext(options: CreateRuntimeContextOptions): RuntimeContext;
111
- /**
112
- * The one place that decides how Mica reaches a configured content
113
- * repository. Every mode produces the same interface, so nothing downstream
114
- * can tell which one it got.
115
- *
116
- * The local editor does not call this — it *is* the local connector — but the
117
- * self-hosted app serves whichever repository its environment names.
118
- */
119
- export declare function createConnector(config: RuntimeConfig): RepositoryConnector;
120
91
  //# sourceMappingURL=context.d.ts.map
@@ -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
@@ -1,60 +1,51 @@
1
1
  /**
2
- * The runtime's composition: one site, its services, and nothing about who
3
- * is allowed in.
4
- *
5
- * Everything is constructed here and passed down explicitly — no module-level
6
- * singletons, no service locator. The composing app builds the connector and,
7
- * where one exists, the draft store and the database; the runtime receives
8
- * them and never asks where they came from. That is what keeps this package
9
- * authentication-neutral and database-free on a developer's machine: the
10
- * local editor simply never hands it either.
2
+ * Builds the runtime's services for one site, with no module-level singletons.
3
+ * The app supplies the working tree's files and the owner policy, so the
4
+ * runtime stays authentication-neutral.
11
5
  */
12
- import { createContentStore, githubConnector, gitlabConnector, localConnector, stagedConnector, } from "@micacms/connectors";
13
6
  import { createLogger } from "./lib/logger.js";
14
7
  import { createRateLimiter } from "./lib/rate-limit.js";
15
8
  import { createMediaService } from "./services/media.js";
16
- import { createDraftStagingService } from "./services/staging.js";
17
- import { createWorkingTreeStagingService } from "./services/staging-local.js";
9
+ import { createContentStore } from "./services/content-store.js";
10
+ import { createWorkingTreeStagingService } from "./services/staging.js";
11
+ import { createWorkspaceLocks, lockedFiles } from "./workspace/locks.js";
18
12
  export const DEFAULT_SITE_ID = "default";
19
13
  export function createRuntimeContext(options) {
20
- const { config, model, drafts } = options;
14
+ const { config, model } = options;
21
15
  const logger = options.logger ??
22
16
  createLogger({
23
17
  level: config.LOG_LEVEL,
24
18
  pretty: !config.isProduction,
25
19
  });
26
20
  const schemaRevision = options.schemaRevision ?? null;
21
+ // Services write through the lock wrapper; staging takes the locks itself.
22
+ const locks = options.locks ?? createWorkspaceLocks();
27
23
  const site = {
28
24
  id: options.siteId ?? DEFAULT_SITE_ID,
29
25
  model,
30
- contentRepository: drafts ? stagedConnector(options.connector, drafts) : options.connector,
26
+ contentRepository: lockedFiles(options.files, locks.tree),
31
27
  schemaRevision,
32
28
  };
33
29
  const services = {
34
30
  content: createContentStore(model, site.contentRepository),
35
31
  media: createMediaService({
36
- connector: site.contentRepository,
32
+ files: site.contentRepository,
37
33
  maxBytes: config.MAX_UPLOAD_BYTES,
38
34
  }),
39
- staging: drafts
40
- ? createDraftStagingService({
41
- model,
42
- drafts,
43
- repository: options.connector,
44
- overlay: site.contentRepository,
45
- schemaRevision,
46
- })
47
- : createWorkingTreeStagingService({
48
- model,
49
- connector: site.contentRepository,
50
- contentRoot: config.contentRoot,
51
- schemaRevision,
52
- publishEnabled: config.MICA_LOCAL_PUBLISH,
53
- logger,
54
- }),
35
+ staging: createWorkingTreeStagingService({
36
+ model,
37
+ files: options.files,
38
+ contentRoot: config.contentRoot,
39
+ schemaRevision,
40
+ publishEnabled: config.MICA_LOCAL_PUBLISH,
41
+ logger,
42
+ locks,
43
+ ...(options.policy ? { policy: options.policy } : {}),
44
+ ...(options.tree ? { tree: options.tree } : {}),
45
+ }),
55
46
  };
56
47
  const limiters = {
57
- // Generous: this exists to blunt runaway scripts, not to shape traffic.
48
+ // Generous: it stops runaway scripts, it does not shape traffic.
58
49
  api: createRateLimiter({ limit: 600, windowMs: 60_000 }),
59
50
  };
60
51
  return {
@@ -64,7 +55,7 @@ export function createRuntimeContext(options) {
64
55
  services,
65
56
  limiters,
66
57
  operational: options.operational ??
67
- (() => ({ auth: "unspecified", database: null })),
58
+ (() => ({ auth: "unspecified" })),
68
59
  health: options.health ?? {},
69
60
  async shutdown() {
70
61
  limiters.api.stop();
@@ -72,46 +63,4 @@ export function createRuntimeContext(options) {
72
63
  },
73
64
  };
74
65
  }
75
- /**
76
- * The one place that decides how Mica reaches a configured content
77
- * repository. Every mode produces the same interface, so nothing downstream
78
- * can tell which one it got.
79
- *
80
- * The local editor does not call this — it *is* the local connector — but the
81
- * self-hosted app serves whichever repository its environment names.
82
- */
83
- export function createConnector(config) {
84
- const author = { name: "Mica CMS", email: "mica@localhost" };
85
- const require = (value, what) => {
86
- if (!value) {
87
- throw new Error(`REPOSITORY is "${config.REPOSITORY}" but ${what} is not set.`);
88
- }
89
- return value;
90
- };
91
- switch (config.REPOSITORY) {
92
- case "github": {
93
- const project = require(config.REPOSITORY_PROJECT, "REPOSITORY_PROJECT");
94
- return githubConnector({
95
- owner: project.split("/")[0],
96
- repo: project.split("/").slice(1).join("/"),
97
- branch: config.REPOSITORY_BRANCH,
98
- root: config.REPOSITORY_ROOT,
99
- token: require(config.REPOSITORY_TOKEN, "REPOSITORY_TOKEN"),
100
- apiBase: config.REPOSITORY_API_BASE,
101
- author,
102
- });
103
- }
104
- case "gitlab":
105
- return gitlabConnector({
106
- project: require(config.REPOSITORY_PROJECT, "REPOSITORY_PROJECT"),
107
- branch: config.REPOSITORY_BRANCH,
108
- root: config.REPOSITORY_ROOT,
109
- token: require(config.REPOSITORY_TOKEN, "REPOSITORY_TOKEN"),
110
- apiBase: config.REPOSITORY_API_BASE,
111
- author,
112
- });
113
- case "local":
114
- return localConnector({ root: config.contentRoot });
115
- }
116
- }
117
66
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
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"]}
package/dist/http.d.ts CHANGED
@@ -1,6 +1,3 @@
1
- /**
2
- * Shared HTTP types and small request helpers.
3
- */
4
1
  import type { Context } from "hono";
5
2
  import type { AccessContext } from "@micacms/core";
6
3
  import type { RuntimeContext } from "./context.js";
@@ -11,31 +8,26 @@ export interface Env {
11
8
  logger: Logger;
12
9
  requestId: string;
13
10
  /**
14
- * Who this request acts as. Set by the auth surface's gate — the OIDC
15
- * gate in the self-hosted app, `allowAll()` in the local editor — and
16
- * therefore present on every admin route. The runtime consumes it and
17
- * never produces it: which is the whole of being authentication-neutral.
11
+ * Who this request acts as. The auth surface's gate sets it on every admin
12
+ * route; the runtime never sets it.
18
13
  */
19
14
  access: AccessContext;
20
15
  };
21
16
  }
22
17
  /**
23
- * Client IP. `X-Forwarded-For` is only consulted when TRUST_PROXY is set —
24
- * otherwise a client could spoof its own address and bypass rate limiting.
25
- * Behind one proxy, the right-most entry is the address that proxy saw; the
26
- * entries to its left are whatever the client sent (#149).
18
+ * Client IP. Reads `X-Forwarded-For` only with TRUST_PROXY, or a client could
19
+ * spoof its address past the rate limit. The right-most entry is the address
20
+ * the proxy saw; the entries to its left come from the client.
27
21
  */
28
22
  export declare function clientIp(c: Context<Env>): string;
29
23
  /**
30
- * Who to name as the author of a commit.
31
- *
32
- * Null in the local editor, where the connector falls back to the developer's
33
- * own Git identity — which is the right name for a change made on their
34
- * machine, and one Mica has no business overwriting.
24
+ * The commit author. Null in the local editor, where the commit keeps the
25
+ * developer's own Git identity.
35
26
  */
36
27
  export declare function actorOf(c: Context<Env>): {
37
28
  id: string;
38
29
  email: string;
30
+ name: string;
39
31
  } | null;
40
32
  /** Parses a positive integer query parameter, falling back when absent. */
41
33
  export declare function intParam(value: string | undefined, fallback: number, max: number): number;
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,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;;;;;WAKG;QACH,MAAM,EAAE,aAAa,CAAC;KACvB,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAWhD;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG7E;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;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIzF"}
package/dist/http.js CHANGED
@@ -1,11 +1,7 @@
1
1
  /**
2
- * Shared HTTP types and small request helpers.
3
- */
4
- /**
5
- * Client IP. `X-Forwarded-For` is only consulted when TRUST_PROXY is set —
6
- * otherwise a client could spoof its own address and bypass rate limiting.
7
- * Behind one proxy, the right-most entry is the address that proxy saw; the
8
- * entries to its left are whatever the client sent (#149).
2
+ * Client IP. Reads `X-Forwarded-For` only with TRUST_PROXY, or a client could
3
+ * spoof its address past the rate limit. The right-most entry is the address
4
+ * the proxy saw; the entries to its left come from the client.
9
5
  */
10
6
  export function clientIp(c) {
11
7
  const { config } = c.get("ctx");
@@ -19,15 +15,12 @@ export function clientIp(c) {
19
15
  return info?.incoming?.socket?.remoteAddress ?? "unknown";
20
16
  }
21
17
  /**
22
- * Who to name as the author of a commit.
23
- *
24
- * Null in the local editor, where the connector falls back to the developer's
25
- * own Git identity — which is the right name for a change made on their
26
- * machine, and one Mica has no business overwriting.
18
+ * The commit author. Null in the local editor, where the commit keeps the
19
+ * developer's own Git identity.
27
20
  */
28
21
  export function actorOf(c) {
29
22
  const identity = c.get("access")?.identity;
30
- return identity ? { id: identity.subject, email: identity.email } : null;
23
+ return identity ? { id: identity.subject, email: identity.email, name: identity.name } : null;
31
24
  }
32
25
  /** Parses a positive integer query parameter, falling back when absent. */
33
26
  export function intParam(value, fallback, max) {
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwBH;;;;;GAKG;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;;;;;;GAMG;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,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3E,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"}
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"]}
package/dist/index.d.ts CHANGED
@@ -1,17 +1,10 @@
1
1
  /**
2
- * @micacms/runtime — the single-site Mica runtime, authentication-neutral.
3
- *
4
- * One Hono API over one `SiteContext`: content, media, staging and system
5
- * routes, with the question "who is this request?" answered by whatever
6
- * `AuthSurface` the composing app hands in. `@micacms/editor` composes it
7
- * with no authentication at all behind a loopback bind; `@micacms/self-hosted`
8
- * composes it behind `@micacms/auth-oidc`. This package must never import an
9
- * auth implementation — that direction is what protects the local
10
- * distribution.
2
+ * @micacms/runtime: the single-site, authentication-neutral Mica runtime.
3
+ * It must never import an auth implementation; the composing app supplies the `AuthSurface`.
11
4
  */
12
5
  export { createApp, allowAll, type AuthSurface, type CreateAppOptions } from "./app.js";
13
- export { createConnector, createRuntimeContext, DEFAULT_SITE_ID, type CreateRuntimeContextOptions, type OperationalInfo, type RuntimeContext, type SiteContext, } from "./context.js";
14
- export { bool, ConfigError, deriveRuntime, describeConfigIssues, list, loadEnvFile, LOOPBACK, requireRepositoryConfig, runtimeEnv, withoutBlanks, type RuntimeConfig, type RuntimeEnv, } from "./config.js";
6
+ export { createRuntimeContext, DEFAULT_SITE_ID, type CreateRuntimeContextOptions, type OperationalInfo, type RuntimeContext, type RuntimeHealth, type SiteContext, } from "./context.js";
7
+ export { bool, ConfigError, deriveRuntime, describeConfigIssues, list, loadEnvFile, LOOPBACK, runtimeEnv, withoutBlanks, type RuntimeConfig, type RuntimeEnv, } from "./config.js";
15
8
  export { startServer, type StartServerOptions } from "./server.js";
16
9
  export { actorOf, clientIp, intParam, type Env } from "./http.js";
17
10
  export { checkSchema, loadSchema, SchemaError, schemaFingerprint, schemaOrigin } from "./schema.js";
@@ -23,9 +16,15 @@ export { createRateLimiter, type RateLimiter } from "./lib/rate-limit.js";
23
16
  export { randomToken } from "./lib/id.js";
24
17
  export { DEFAULT_BASE_URL, DEFAULT_PORT } from "./lib/ports.js";
25
18
  export { VERSION } from "./lib/version.js";
26
- export { openSqlite, type RunResult, type SqlDatabase, type SqlParams, type SqlValue } from "./db/driver.js";
27
- export { createDraftStagingService, analyseChanges, classifyPath, type AnnotatedChange, type ChangeOwner, type ContentStagingService, type DraftStagingDeps, type PublishInput, type StagingProblem, type StagingStatus, type StagingValidation, } from "./services/staging.js";
28
- export { createWorkingTreeStagingService, type WorkingTreeStagingDeps, } from "./services/staging-local.js";
19
+ export { analyseChanges, classifyPath, describeProblems, flattenProblems, type AnnotatedChange, type ChangeOwner, type ContentStagingService, type RepositoryInfo, type PublishInput, type StagingProblem, type StagingStatus, type StagingValidation, } from "./services/changes.js";
20
+ export { createWorkingTreeStagingService, type WorkingTreeStagingDeps, } from "./services/staging.js";
29
21
  export { createMediaService, formatBytes, type MediaFile, type MediaService } from "./services/media.js";
22
+ export { createContentStore, ContentError, ValidationError, type ContentStore, type SavedEntry, type SaveEntryInput, } from "./services/content-store.js";
23
+ export { checkChangePaths, ConflictError, contentFiles, FilesError, type Author, type ContentFiles, type ContentFilesOptions, type FileChange, type FileChanges, } from "./workspace/files.js";
24
+ export { developerEnvironment, firstLine, GitError, gitRunner, type GitCallOptions, type GitRunner, type GitRunnerOptions, } from "./workspace/git.js";
25
+ export { createMutex, createReadWriteLock, createWorkspaceLocks, lockedFiles, type Mutex, type ReadWriteLock, type WorkspaceLocks, } from "./workspace/locks.js";
26
+ export type { OwnerPolicy, PublishAvailability, RepositoryDescription, SyncConflict, SyncStatus, } from "./workspace/policy.js";
27
+ export { developerPolicy, type DeveloperPolicyOptions } from "./workspace/developer.js";
28
+ export { commitPaths, headRevision, headSource, inRepo, listTreeChanges, LITERAL, locateTree, parseStatus, recommitPaths, type GitTree, type TreeChange, } from "./workspace/tree.js";
30
29
  export { schemaStats } from "./lib/schema-stats.js";
31
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACxF,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,uBAAuB,EACvB,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,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,YAAY,EACZ,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,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,6BAA6B,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACzG,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
@@ -1,17 +1,10 @@
1
1
  /**
2
- * @micacms/runtime — the single-site Mica runtime, authentication-neutral.
3
- *
4
- * One Hono API over one `SiteContext`: content, media, staging and system
5
- * routes, with the question "who is this request?" answered by whatever
6
- * `AuthSurface` the composing app hands in. `@micacms/editor` composes it
7
- * with no authentication at all behind a loopback bind; `@micacms/self-hosted`
8
- * composes it behind `@micacms/auth-oidc`. This package must never import an
9
- * auth implementation — that direction is what protects the local
10
- * distribution.
2
+ * @micacms/runtime: the single-site, authentication-neutral Mica runtime.
3
+ * It must never import an auth implementation; the composing app supplies the `AuthSurface`.
11
4
  */
12
5
  export { createApp, allowAll } from "./app.js";
13
- export { createConnector, createRuntimeContext, DEFAULT_SITE_ID, } from "./context.js";
14
- export { bool, ConfigError, deriveRuntime, describeConfigIssues, list, loadEnvFile, LOOPBACK, requireRepositoryConfig, runtimeEnv, withoutBlanks, } from "./config.js";
6
+ export { createRuntimeContext, DEFAULT_SITE_ID, } from "./context.js";
7
+ export { bool, ConfigError, deriveRuntime, describeConfigIssues, list, loadEnvFile, LOOPBACK, runtimeEnv, withoutBlanks, } from "./config.js";
15
8
  export { startServer } from "./server.js";
16
9
  export { actorOf, clientIp, intParam } from "./http.js";
17
10
  export { checkSchema, loadSchema, SchemaError, schemaFingerprint, schemaOrigin } from "./schema.js";
@@ -23,9 +16,14 @@ export { createRateLimiter } from "./lib/rate-limit.js";
23
16
  export { randomToken } from "./lib/id.js";
24
17
  export { DEFAULT_BASE_URL, DEFAULT_PORT } from "./lib/ports.js";
25
18
  export { VERSION } from "./lib/version.js";
26
- export { openSqlite } from "./db/driver.js";
27
- export { createDraftStagingService, analyseChanges, classifyPath, } from "./services/staging.js";
28
- export { createWorkingTreeStagingService, } from "./services/staging-local.js";
19
+ export { analyseChanges, classifyPath, describeProblems, flattenProblems, } from "./services/changes.js";
20
+ export { createWorkingTreeStagingService, } from "./services/staging.js";
29
21
  export { createMediaService, formatBytes } from "./services/media.js";
22
+ export { createContentStore, ContentError, ValidationError, } from "./services/content-store.js";
23
+ export { checkChangePaths, ConflictError, contentFiles, FilesError, } from "./workspace/files.js";
24
+ export { developerEnvironment, firstLine, GitError, gitRunner, } from "./workspace/git.js";
25
+ export { createMutex, createReadWriteLock, createWorkspaceLocks, lockedFiles, } from "./workspace/locks.js";
26
+ export { developerPolicy } from "./workspace/developer.js";
27
+ export { commitPaths, headRevision, headSource, inRepo, listTreeChanges, LITERAL, locateTree, parseStatus, recommitPaths, } from "./workspace/tree.js";
30
28
  export { schemaStats } from "./lib/schema-stats.js";
31
29
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAA2C,MAAM,UAAU,CAAC;AACxF,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,eAAe,GAKhB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,uBAAuB,EACvB,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,EAAE,UAAU,EAAmE,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,YAAY,GASb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,GAEhC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAqC,MAAM,qBAAqB,CAAC;AACzG,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
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 +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"}
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 +1 @@
1
- {"version":3,"file":"file-type.js","sourceRoot":"","sources":["../../src/lib/file-type.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-type.js","sourceRoot":"","sources":["../../src/lib/file-type.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Content sniffing for uploads.\n *\n * The browser-supplied MIME type is attacker-controlled and must not be\n * trusted: an executable renamed to `.jpg` arrives as `image/jpeg`. The stored\n * type is derived from the bytes instead, and the extension is regenerated\n * from it rather than taken from the filename.\n */\n\nexport interface DetectedType {\n mimeType: string;\n extension: string;\n}\n\nconst startsWith = (bytes: Uint8Array, signature: number[], offset = 0): boolean =>\n signature.every((byte, index) => bytes[offset + index] === byte);\n\nconst ascii = (bytes: Uint8Array, offset: number, length: number): string =>\n Array.from(bytes.slice(offset, offset + length), (byte) => String.fromCharCode(byte)).join(\"\");\n\nexport function detectFileType(bytes: Uint8Array): DetectedType | null {\n if (bytes.length < 12) return null;\n\n if (startsWith(bytes, [0xff, 0xd8, 0xff])) {\n return { mimeType: \"image/jpeg\", extension: \"jpg\" };\n }\n\n if (startsWith(bytes, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) {\n return { mimeType: \"image/png\", extension: \"png\" };\n }\n\n const header = ascii(bytes, 0, 6);\n if (header === \"GIF87a\" || header === \"GIF89a\") {\n return { mimeType: \"image/gif\", extension: \"gif\" };\n }\n\n if (ascii(bytes, 0, 4) === \"RIFF\" && ascii(bytes, 8, 4) === \"WEBP\") {\n return { mimeType: \"image/webp\", extension: \"webp\" };\n }\n\n // ISO base media formats declare their brand at offset 8, after the box size.\n if (ascii(bytes, 4, 4) === \"ftyp\") {\n const brand = ascii(bytes, 8, 4);\n if (brand === \"avif\" || brand === \"avis\") {\n return { mimeType: \"image/avif\", extension: \"avif\" };\n }\n if (brand === \"heic\" || brand === \"heix\" || brand === \"mif1\") {\n return { mimeType: \"image/heic\", extension: \"heic\" };\n }\n if (brand === \"isom\" || brand === \"mp42\" || brand === \"M4V \") {\n return { mimeType: \"video/mp4\", extension: \"mp4\" };\n }\n }\n\n if (ascii(bytes, 0, 5) === \"%PDF-\") {\n return { mimeType: \"application/pdf\", extension: \"pdf\" };\n }\n\n if (isSvg(bytes)) {\n return { mimeType: \"image/svg+xml\", extension: \"svg\" };\n }\n\n return null;\n}\n\n/** SVG has no magic number, so look for a root `<svg>` in the leading text. */\nfunction isSvg(bytes: Uint8Array): boolean {\n const head = new TextDecoder(\"utf-8\", { fatal: false })\n .decode(bytes.slice(0, 1024))\n .trimStart()\n .toLowerCase();\n\n if (head.startsWith(\"<svg\")) return true;\n if (head.startsWith(\"<?xml\") || head.startsWith(\"<!doctype svg\")) {\n return head.includes(\"<svg\");\n }\n return false;\n}\n\nconst IMAGE_TYPES = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"image/avif\",\n \"image/heic\",\n]);\n\nconst DOCUMENT_TYPES = new Set([\"application/pdf\"]);\n\nexport const isImageType = (mimeType: string): boolean => IMAGE_TYPES.has(mimeType);\n\n/**\n * Whether a detected type is acceptable for a field's `accept` setting.\n * `any` deliberately does not mean \"anything\": only the types listed here can\n * be stored at all.\n */\nexport function isAccepted(mimeType: string, accept: \"image\" | \"document\" | \"any\"): boolean {\n if (accept === \"image\") return IMAGE_TYPES.has(mimeType);\n if (accept === \"document\") return DOCUMENT_TYPES.has(mimeType);\n return IMAGE_TYPES.has(mimeType) || DOCUMENT_TYPES.has(mimeType) || mimeType === \"video/mp4\";\n}\n\n/**\n * SVG can carry scripts, so Mica neither stores nor shows it (#86). It is still\n * detected, so that the refusal can say why.\n */\nexport const SVG_TYPE = \"image/svg+xml\";\n"]}