@micacms/runtime 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +16 -0
  2. package/dist/app.d.ts +62 -0
  3. package/dist/app.d.ts.map +1 -0
  4. package/dist/app.js +107 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/config.d.ts +187 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +154 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/context.d.ts +121 -0
  11. package/dist/context.d.ts.map +1 -0
  12. package/dist/context.js +117 -0
  13. package/dist/context.js.map +1 -0
  14. package/dist/db/driver.d.ts +35 -0
  15. package/dist/db/driver.d.ts.map +1 -0
  16. package/dist/db/driver.js +95 -0
  17. package/dist/db/driver.js.map +1 -0
  18. package/dist/http.d.ts +40 -0
  19. package/dist/http.d.ts.map +1 -0
  20. package/dist/http.js +38 -0
  21. package/dist/http.js.map +1 -0
  22. package/dist/index.d.ts +31 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +31 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/lib/errors.d.ts +39 -0
  27. package/dist/lib/errors.d.ts.map +1 -0
  28. package/dist/lib/errors.js +52 -0
  29. package/dist/lib/errors.js.map +1 -0
  30. package/dist/lib/file-type.d.ts +26 -0
  31. package/dist/lib/file-type.d.ts.map +1 -0
  32. package/dist/lib/file-type.js +89 -0
  33. package/dist/lib/file-type.js.map +1 -0
  34. package/dist/lib/git.d.ts +29 -0
  35. package/dist/lib/git.d.ts.map +1 -0
  36. package/dist/lib/git.js +62 -0
  37. package/dist/lib/git.js.map +1 -0
  38. package/dist/lib/id.d.ts +13 -0
  39. package/dist/lib/id.d.ts.map +1 -0
  40. package/dist/lib/id.js +56 -0
  41. package/dist/lib/id.js.map +1 -0
  42. package/dist/lib/logger.d.ts +20 -0
  43. package/dist/lib/logger.d.ts.map +1 -0
  44. package/dist/lib/logger.js +65 -0
  45. package/dist/lib/logger.js.map +1 -0
  46. package/dist/lib/ports.d.ts +17 -0
  47. package/dist/lib/ports.d.ts.map +1 -0
  48. package/dist/lib/ports.js +17 -0
  49. package/dist/lib/ports.js.map +1 -0
  50. package/dist/lib/rate-limit.d.ts +29 -0
  51. package/dist/lib/rate-limit.d.ts.map +1 -0
  52. package/dist/lib/rate-limit.js +47 -0
  53. package/dist/lib/rate-limit.js.map +1 -0
  54. package/dist/lib/schema-stats.d.ts +64 -0
  55. package/dist/lib/schema-stats.d.ts.map +1 -0
  56. package/dist/lib/schema-stats.js +103 -0
  57. package/dist/lib/schema-stats.js.map +1 -0
  58. package/dist/lib/version.d.ts +14 -0
  59. package/dist/lib/version.d.ts.map +1 -0
  60. package/dist/lib/version.js +25 -0
  61. package/dist/lib/version.js.map +1 -0
  62. package/dist/middleware/index.d.ts +41 -0
  63. package/dist/middleware/index.d.ts.map +1 -0
  64. package/dist/middleware/index.js +142 -0
  65. package/dist/middleware/index.js.map +1 -0
  66. package/dist/routes/admin-content.d.ts +22 -0
  67. package/dist/routes/admin-content.d.ts.map +1 -0
  68. package/dist/routes/admin-content.js +194 -0
  69. package/dist/routes/admin-content.js.map +1 -0
  70. package/dist/routes/admin-media.d.ts +19 -0
  71. package/dist/routes/admin-media.d.ts.map +1 -0
  72. package/dist/routes/admin-media.js +81 -0
  73. package/dist/routes/admin-media.js.map +1 -0
  74. package/dist/routes/admin-spa.d.ts +13 -0
  75. package/dist/routes/admin-spa.d.ts.map +1 -0
  76. package/dist/routes/admin-spa.js +72 -0
  77. package/dist/routes/admin-spa.js.map +1 -0
  78. package/dist/routes/admin-staging.d.ts +15 -0
  79. package/dist/routes/admin-staging.d.ts.map +1 -0
  80. package/dist/routes/admin-staging.js +90 -0
  81. package/dist/routes/admin-staging.js.map +1 -0
  82. package/dist/routes/admin-system.d.ts +14 -0
  83. package/dist/routes/admin-system.d.ts.map +1 -0
  84. package/dist/routes/admin-system.js +153 -0
  85. package/dist/routes/admin-system.js.map +1 -0
  86. package/dist/routes/health.d.ts +12 -0
  87. package/dist/routes/health.d.ts.map +1 -0
  88. package/dist/routes/health.js +44 -0
  89. package/dist/routes/health.js.map +1 -0
  90. package/dist/schema.d.ts +56 -0
  91. package/dist/schema.d.ts.map +1 -0
  92. package/dist/schema.js +173 -0
  93. package/dist/schema.js.map +1 -0
  94. package/dist/server.d.ts +28 -0
  95. package/dist/server.d.ts.map +1 -0
  96. package/dist/server.js +47 -0
  97. package/dist/server.js.map +1 -0
  98. package/dist/services/media.d.ts +65 -0
  99. package/dist/services/media.d.ts.map +1 -0
  100. package/dist/services/media.js +180 -0
  101. package/dist/services/media.js.map +1 -0
  102. package/dist/services/staging-local.d.ts +58 -0
  103. package/dist/services/staging-local.d.ts.map +1 -0
  104. package/dist/services/staging-local.js +391 -0
  105. package/dist/services/staging-local.js.map +1 -0
  106. package/dist/services/staging.d.ts +167 -0
  107. package/dist/services/staging.d.ts.map +1 -0
  108. package/dist/services/staging.js +333 -0
  109. package/dist/services/staging.js.map +1 -0
  110. package/package.json +49 -0
@@ -0,0 +1,121 @@
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.
11
+ */
12
+ import { type ContentStore } from "@micacms/connectors";
13
+ import type { DraftStore, RepositoryConnector, Site } from "@micacms/core";
14
+ import type { RuntimeConfig } from "./config.js";
15
+ import { type Logger } from "./lib/logger.js";
16
+ import { type RateLimiter } from "./lib/rate-limit.js";
17
+ 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
+ */
27
+ export interface SiteContext {
28
+ /** Identifies this site in logs. Constant while one runtime serves one site. */
29
+ id: string;
30
+ /** The content model, read from the repository at startup. */
31
+ 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;
38
+ /**
39
+ * Revision of `.mica/schema.json` when the model was loaded. The publish
40
+ * drift gate compares against it — see decisions.md.
41
+ */
42
+ schemaRevision: string | null;
43
+ }
44
+ 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
+ */
51
+ export interface OperationalInfo {
52
+ /** How this deployment authenticates, in words. */
53
+ auth: string;
54
+ /** Null where there is no database, which is the local editor's answer. */
55
+ database: {
56
+ path: string;
57
+ sessions: number;
58
+ version: number;
59
+ bytes: number | null;
60
+ } | null;
61
+ /** What decides who may edit, in the operator's own words. */
62
+ admission?: string;
63
+ dataDir?: string;
64
+ sessionTtlHours?: number;
65
+ sessionIdleHours?: number;
66
+ /** Names of the secrets this process generated for itself. */
67
+ generatedSecrets?: string[];
68
+ }
69
+ export interface RuntimeContext {
70
+ config: RuntimeConfig;
71
+ site: SiteContext;
72
+ logger: Logger;
73
+ services: {
74
+ content: ContentStore;
75
+ media: MediaService;
76
+ staging: ContentStagingService;
77
+ };
78
+ limiters: {
79
+ api: RateLimiter;
80
+ };
81
+ /** App-supplied: what the System page says about the composition. */
82
+ operational(): OperationalInfo;
83
+ /** App-supplied active probes for /health. Absent means nothing to probe. */
84
+ health: {
85
+ database?: () => void;
86
+ };
87
+ /** Releases timers, and whatever the app added on top. */
88
+ shutdown(): Promise<void>;
89
+ }
90
+ export interface CreateRuntimeContextOptions {
91
+ config: RuntimeConfig;
92
+ model: Site;
93
+ /** How the content repository is reached. The app builds it. */
94
+ connector: RepositoryConnector;
95
+ /**
96
+ * Where saves accumulate when the repository is remote. Absent means the
97
+ * working tree is the draft workspace — the local editor, or a self-hosted
98
+ * deployment with a checkout beside it.
99
+ */
100
+ drafts?: DraftStore;
101
+ /** Revision of the manifest the model was read from. Null when unknown. */
102
+ schemaRevision?: string | null;
103
+ siteId?: string;
104
+ logger?: Logger;
105
+ operational?: () => OperationalInfo;
106
+ health?: {
107
+ database?: () => void;
108
+ };
109
+ onShutdown?: () => void | Promise<void>;
110
+ }
111
+ export declare function createRuntimeContext(options: CreateRuntimeContextOptions): RuntimeContext;
112
+ /**
113
+ * The one place that decides how Mica reaches a configured content
114
+ * repository. Every mode produces the same interface, so nothing downstream
115
+ * can tell which one it got.
116
+ *
117
+ * The local editor does not call this — it *is* the local connector — but the
118
+ * self-hosted app serves whichever repository its environment names.
119
+ */
120
+ export declare function createConnector(config: RuntimeConfig): RepositoryConnector;
121
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
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.
11
+ */
12
+ import { createContentStore, githubConnector, gitlabConnector, localConnector, stagedConnector, } from "@micacms/connectors";
13
+ import { createLogger } from "./lib/logger.js";
14
+ import { createRateLimiter } from "./lib/rate-limit.js";
15
+ import { createMediaService } from "./services/media.js";
16
+ import { createDraftStagingService } from "./services/staging.js";
17
+ import { createWorkingTreeStagingService } from "./services/staging-local.js";
18
+ export const DEFAULT_SITE_ID = "default";
19
+ export function createRuntimeContext(options) {
20
+ const { config, model, drafts } = options;
21
+ const logger = options.logger ??
22
+ createLogger({
23
+ level: config.LOG_LEVEL,
24
+ pretty: !config.isProduction,
25
+ });
26
+ const schemaRevision = options.schemaRevision ?? null;
27
+ const site = {
28
+ id: options.siteId ?? DEFAULT_SITE_ID,
29
+ model,
30
+ contentRepository: drafts ? stagedConnector(options.connector, drafts) : options.connector,
31
+ schemaRevision,
32
+ };
33
+ const services = {
34
+ content: createContentStore(model, site.contentRepository),
35
+ media: createMediaService({
36
+ connector: site.contentRepository,
37
+ maxBytes: config.MAX_UPLOAD_BYTES,
38
+ }),
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
+ }),
55
+ };
56
+ const limiters = {
57
+ // Generous: this exists to blunt runaway scripts, not to shape traffic.
58
+ api: createRateLimiter({ limit: 600, windowMs: 60_000 }),
59
+ };
60
+ return {
61
+ config,
62
+ site,
63
+ logger,
64
+ services,
65
+ limiters,
66
+ operational: options.operational ??
67
+ (() => ({ auth: "unspecified", database: null })),
68
+ health: options.health ?? {},
69
+ async shutdown() {
70
+ limiters.api.stop();
71
+ await options.onShutdown?.();
72
+ },
73
+ };
74
+ }
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
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * SQLite driver.
3
+ *
4
+ * Everything above this file talks to `SqlDatabase`, never to `node:sqlite`
5
+ * directly, so the driver can be swapped (better-sqlite3, libsql, a Postgres
6
+ * adapter) without touching the repositories.
7
+ *
8
+ * `node:sqlite` is the default because it ships with Node — no native build
9
+ * step, no prebuilt-binary mismatch inside the container.
10
+ */
11
+ export type SqlValue = string | number | bigint | null | Uint8Array;
12
+ export type SqlParams = readonly SqlValue[];
13
+ export interface RunResult {
14
+ changes: number;
15
+ lastInsertRowid: number | bigint;
16
+ }
17
+ export interface SqlDatabase {
18
+ all<T = Record<string, unknown>>(sql: string, params?: SqlParams): T[];
19
+ get<T = Record<string, unknown>>(sql: string, params?: SqlParams): T | undefined;
20
+ run(sql: string, params?: SqlParams): RunResult;
21
+ exec(sql: string): void;
22
+ /** Runs `fn` in a transaction, rolling back if it throws. Safe to nest. */
23
+ transaction<T>(fn: () => T): T;
24
+ /** Consistent on-line snapshot to `destination`, safe while writes continue. */
25
+ snapshot(destination: string): void;
26
+ close(): void;
27
+ }
28
+ export interface SqliteOptions {
29
+ /** Milliseconds a blocked writer waits before returning SQLITE_BUSY. */
30
+ busyTimeoutMs?: number;
31
+ /** WAL is the right default for a server; `false` is useful for :memory:. */
32
+ wal?: boolean;
33
+ }
34
+ export declare function openSqlite(filename: string, options?: SqliteOptions): SqlDatabase;
35
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/db/driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,UAAU,CAAC;AACpE,MAAM,MAAM,SAAS,GAAG,SAAS,QAAQ,EAAE,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,CAAC;IACvE,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,CAAC,GAAG,SAAS,CAAC;IACjF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,2EAA2E;IAC3E,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,WAAW,CA2FrF"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * SQLite driver.
3
+ *
4
+ * Everything above this file talks to `SqlDatabase`, never to `node:sqlite`
5
+ * directly, so the driver can be swapped (better-sqlite3, libsql, a Postgres
6
+ * adapter) without touching the repositories.
7
+ *
8
+ * `node:sqlite` is the default because it ships with Node — no native build
9
+ * step, no prebuilt-binary mismatch inside the container.
10
+ */
11
+ import { DatabaseSync } from "node:sqlite";
12
+ export function openSqlite(filename, options = {}) {
13
+ const db = new DatabaseSync(filename);
14
+ const cache = new Map();
15
+ let depth = 0;
16
+ db.exec("PRAGMA foreign_keys = ON");
17
+ db.exec(`PRAGMA busy_timeout = ${options.busyTimeoutMs ?? 5000}`);
18
+ if (options.wal !== false) {
19
+ // WAL lets the Astro build read while an editor writes.
20
+ db.exec("PRAGMA journal_mode = WAL");
21
+ // NORMAL is durable across process crashes; only an OS-level crash can lose
22
+ // the last transactions. The right trade-off for a content database.
23
+ db.exec("PRAGMA synchronous = NORMAL");
24
+ }
25
+ const prepare = (sql) => {
26
+ let statement = cache.get(sql);
27
+ if (!statement) {
28
+ statement = db.prepare(sql);
29
+ cache.set(sql, statement);
30
+ }
31
+ return statement;
32
+ };
33
+ // Rows arrive as null-prototype objects; callers expect ordinary ones.
34
+ const plain = (row) => ({ ...row });
35
+ return {
36
+ all(sql, params = []) {
37
+ return prepare(sql)
38
+ .all(...params)
39
+ .map((row) => plain(row));
40
+ },
41
+ get(sql, params = []) {
42
+ const row = prepare(sql).get(...params);
43
+ return row === undefined ? undefined : plain(row);
44
+ },
45
+ run(sql, params = []) {
46
+ const result = prepare(sql).run(...params);
47
+ return {
48
+ changes: Number(result.changes),
49
+ lastInsertRowid: result.lastInsertRowid,
50
+ };
51
+ },
52
+ exec(sql) {
53
+ db.exec(sql);
54
+ },
55
+ transaction(fn) {
56
+ // Nested calls use savepoints so an inner rollback does not discard the
57
+ // outer transaction's work.
58
+ const name = `sp_${depth}`;
59
+ const begin = depth === 0 ? "BEGIN IMMEDIATE" : `SAVEPOINT ${name}`;
60
+ const commit = depth === 0 ? "COMMIT" : `RELEASE ${name}`;
61
+ const rollback = depth === 0 ? "ROLLBACK" : `ROLLBACK TO ${name}`;
62
+ db.exec(begin);
63
+ depth++;
64
+ try {
65
+ const result = fn();
66
+ depth--;
67
+ db.exec(commit);
68
+ return result;
69
+ }
70
+ catch (error) {
71
+ depth--;
72
+ try {
73
+ db.exec(rollback);
74
+ if (depth > 0)
75
+ db.exec(`RELEASE ${name}`);
76
+ }
77
+ catch {
78
+ // The rollback itself failing means the connection is unusable;
79
+ // surface the original error rather than masking it.
80
+ }
81
+ throw error;
82
+ }
83
+ },
84
+ snapshot(destination) {
85
+ // VACUUM INTO takes a read lock only, so editors are not blocked and the
86
+ // resulting file is a defragmented, fully consistent database.
87
+ db.exec(`VACUUM INTO '${destination.replace(/'/g, "''")}'`);
88
+ },
89
+ close() {
90
+ cache.clear();
91
+ db.close();
92
+ },
93
+ };
94
+ }
95
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/db/driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AA6B/D,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,UAAyB,EAAE;IACtE,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACpC,EAAE,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC,CAAC;IAElE,IAAI,OAAO,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;QAC1B,wDAAwD;QACxD,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACrC,4EAA4E;QAC5E,qEAAqE;QACrE,EAAE,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAW,EAAiB,EAAE;QAC7C,IAAI,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,SAAS,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC5B,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IAEF,uEAAuE;IACvE,MAAM,KAAK,GAAG,CAAI,GAAY,EAAK,EAAE,CAAC,CAAC,EAAE,GAAI,GAAc,EAAE,CAAM,CAAC;IAEpE,OAAO;QACL,GAAG,CAAI,GAAW,EAAE,SAAoB,EAAE;YACxC,OAAO,OAAO,CAAC,GAAG,CAAC;iBAChB,GAAG,CAAC,GAAI,MAAqB,CAAC;iBAC9B,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAI,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC;QAED,GAAG,CAAI,GAAW,EAAE,SAAoB,EAAE;YACxC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAI,MAAqB,CAAC,CAAC;YACxD,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAI,GAAG,CAAC,CAAC;QACvD,CAAC;QAED,GAAG,CAAC,GAAW,EAAE,SAAoB,EAAE;YACrC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAI,MAAqB,CAAC,CAAC;YAC3D,OAAO;gBACL,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;gBAC/B,eAAe,EAAE,MAAM,CAAC,eAAe;aACxC,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,GAAW;YACd,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;QAED,WAAW,CAAI,EAAW;YACxB,wEAAwE;YACxE,4BAA4B;YAC5B,MAAM,IAAI,GAAG,MAAM,KAAK,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,aAAa,IAAI,EAAE,CAAC;YACpE,MAAM,MAAM,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC;YAC1D,MAAM,QAAQ,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,IAAI,EAAE,CAAC;YAElE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACf,KAAK,EAAE,CAAC;YACR,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,EAAE,EAAE,CAAC;gBACpB,KAAK,EAAE,CAAC;gBACR,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAChB,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,EAAE,CAAC;gBACR,IAAI,CAAC;oBACH,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAClB,IAAI,KAAK,GAAG,CAAC;wBAAE,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;gBAC5C,CAAC;gBAAC,MAAM,CAAC;oBACP,gEAAgE;oBAChE,qDAAqD;gBACvD,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QAED,QAAQ,CAAC,WAAmB;YAC1B,yEAAyE;YACzE,+DAA+D;YAC/D,EAAE,CAAC,IAAI,CAAC,gBAAgB,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9D,CAAC;QAED,KAAK;YACH,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Shared HTTP types and small request helpers.
3
+ */
4
+ import type { Context } from "hono";
5
+ import type { AccessContext } from "@micacms/core";
6
+ import type { RuntimeContext } from "./context.js";
7
+ import type { Logger } from "./lib/logger.js";
8
+ export interface Env {
9
+ Variables: {
10
+ ctx: RuntimeContext;
11
+ logger: Logger;
12
+ requestId: string;
13
+ /**
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.
18
+ */
19
+ access: AccessContext;
20
+ };
21
+ }
22
+ /**
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
+ */
26
+ export declare function clientIp(c: Context<Env>): string;
27
+ /**
28
+ * Who to name as the author of a commit.
29
+ *
30
+ * Null in the local editor, where the connector falls back to the developer's
31
+ * own Git identity — which is the right name for a change made on their
32
+ * machine, and one Mica has no business overwriting.
33
+ */
34
+ export declare function actorOf(c: Context<Env>): {
35
+ id: string;
36
+ email: string;
37
+ } | null;
38
+ /** Parses a positive integer query parameter, falling back when absent. */
39
+ export declare function intParam(value: string | undefined, fallback: number, max: number): number;
40
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +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;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAYhD;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"}
package/dist/http.js ADDED
@@ -0,0 +1,38 @@
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
+ */
8
+ export function clientIp(c) {
9
+ const { config } = c.get("ctx");
10
+ if (config.TRUST_PROXY) {
11
+ const forwarded = c.req.header("x-forwarded-for");
12
+ // The left-most entry is the original client; the rest are proxies.
13
+ const first = forwarded?.split(",")[0]?.trim();
14
+ if (first)
15
+ return first;
16
+ }
17
+ const info = c.env;
18
+ return info?.incoming?.socket?.remoteAddress ?? "unknown";
19
+ }
20
+ /**
21
+ * Who to name as the author of a commit.
22
+ *
23
+ * Null in the local editor, where the connector falls back to the developer's
24
+ * own Git identity — which is the right name for a change made on their
25
+ * machine, and one Mica has no business overwriting.
26
+ */
27
+ export function actorOf(c) {
28
+ const identity = c.get("access")?.identity;
29
+ return identity ? { id: identity.subject, email: identity.email } : null;
30
+ }
31
+ /** Parses a positive integer query parameter, falling back when absent. */
32
+ export function intParam(value, fallback, max) {
33
+ const parsed = Number(value);
34
+ if (!Number.isFinite(parsed) || parsed < 0)
35
+ return fallback;
36
+ return Math.min(Math.floor(parsed), max);
37
+ }
38
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwBH;;;GAGG;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,oEAAoE;QACpE,MAAM,KAAK,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QAC/C,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,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"}
@@ -0,0 +1,31 @@
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.
11
+ */
12
+ 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";
15
+ export { startServer, type StartServerOptions } from "./server.js";
16
+ export { actorOf, clientIp, intParam, type Env } from "./http.js";
17
+ export { checkSchema, loadSchema, SchemaError, schemaFingerprint, schemaOrigin } from "./schema.js";
18
+ export { allowedOrigins, errorHandler, originGuard, rateLimit, requestContext, securityHeaders, } from "./middleware/index.js";
19
+ export { adminAssetsExist } from "./routes/admin-spa.js";
20
+ export { ApiError, badRequest, conflict, forbidden, notFound, unauthenticated, validationFailed, type ApiErrorBody, type ErrorCode, } from "./lib/errors.js";
21
+ export { createLogger, type Logger } from "./lib/logger.js";
22
+ export { createRateLimiter, type RateLimiter } from "./lib/rate-limit.js";
23
+ export { randomToken } from "./lib/id.js";
24
+ export { DEFAULT_BASE_URL, DEFAULT_PORT } from "./lib/ports.js";
25
+ 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";
29
+ export { createMediaService, formatBytes, type MediaFile, type MediaService } from "./services/media.js";
30
+ export { schemaStats } from "./lib/schema-stats.js";
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
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.
11
+ */
12
+ 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";
15
+ export { startServer } from "./server.js";
16
+ export { actorOf, clientIp, intParam } from "./http.js";
17
+ export { checkSchema, loadSchema, SchemaError, schemaFingerprint, schemaOrigin } from "./schema.js";
18
+ export { allowedOrigins, errorHandler, originGuard, rateLimit, requestContext, securityHeaders, } from "./middleware/index.js";
19
+ export { adminAssetsExist } from "./routes/admin-spa.js";
20
+ export { ApiError, badRequest, conflict, forbidden, notFound, unauthenticated, validationFailed, } from "./lib/errors.js";
21
+ export { createLogger } from "./lib/logger.js";
22
+ export { createRateLimiter } from "./lib/rate-limit.js";
23
+ export { randomToken } from "./lib/id.js";
24
+ export { DEFAULT_BASE_URL, DEFAULT_PORT } from "./lib/ports.js";
25
+ 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";
29
+ export { createMediaService, formatBytes } from "./services/media.js";
30
+ export { schemaStats } from "./lib/schema-stats.js";
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Error taxonomy.
3
+ *
4
+ * Every failure the API reports on purpose is an `ApiError` carrying a stable
5
+ * machine-readable `code`. Anything else that reaches the error middleware is
6
+ * treated as a bug: logged with its stack, reported as a bare 500.
7
+ */
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"
10
+ /** An identity provider Mica depends on could not be reached. */
11
+ | "service_unavailable";
12
+ export interface ApiErrorBody {
13
+ error: {
14
+ code: ErrorCode;
15
+ message: string;
16
+ issues?: FieldIssue[];
17
+ };
18
+ }
19
+ export declare class ApiError extends Error {
20
+ name: string;
21
+ readonly code: ErrorCode;
22
+ readonly status: number;
23
+ readonly issues?: FieldIssue[];
24
+ /** Extra response headers, e.g. `Retry-After` on a rate limit. */
25
+ readonly headers?: Record<string, string>;
26
+ constructor(code: ErrorCode, message: string, options?: {
27
+ issues?: FieldIssue[];
28
+ headers?: Record<string, string>;
29
+ cause?: unknown;
30
+ });
31
+ toBody(): ApiErrorBody;
32
+ }
33
+ export declare const badRequest: (message: string) => ApiError;
34
+ export declare const unauthenticated: (message?: string) => ApiError;
35
+ export declare const forbidden: (message?: string) => ApiError;
36
+ export declare const notFound: (message?: string) => ApiError;
37
+ export declare const conflict: (message: string) => ApiError;
38
+ export declare const validationFailed: (issues: FieldIssue[], message?: string) => ApiError;
39
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +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"}