@demiurgejs/core 0.2.0-beta.4 → 0.2.0-beta.6

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 (161) hide show
  1. package/README.md +37 -0
  2. package/bin/demiurge.mjs +23 -1
  3. package/dist/adapter/index.d.ts +1 -1
  4. package/dist/adapter/index.d.ts.map +1 -1
  5. package/dist/browser/file-router.d.ts +1 -1
  6. package/dist/chunks/{document-shell-CpJnFtSC.js → document-shell-C0aPrkhd.js} +2 -2
  7. package/dist/chunks/{document-shell-CpJnFtSC.js.map → document-shell-C0aPrkhd.js.map} +1 -1
  8. package/dist/chunks/image-CRerJ5BY.js +145 -0
  9. package/dist/chunks/image-CRerJ5BY.js.map +1 -0
  10. package/dist/chunks/{image-codec-BmzWnH6W.js → image-codec-CQ8Cwxwb.js} +2 -2
  11. package/dist/chunks/{image-codec-BmzWnH6W.js.map → image-codec-CQ8Cwxwb.js.map} +1 -1
  12. package/dist/chunks/{images-BQ80950B.js → images-QgdmLyQ3.js} +11 -11
  13. package/dist/chunks/{images-BQ80950B.js.map → images-QgdmLyQ3.js.map} +1 -1
  14. package/dist/chunks/{index-BVk9zWbe.js → index-LYve1rQp.js} +287 -351
  15. package/dist/chunks/index-LYve1rQp.js.map +1 -0
  16. package/dist/chunks/links-iU22QkCE.js +68 -0
  17. package/dist/chunks/links-iU22QkCE.js.map +1 -0
  18. package/dist/chunks/plugin-bkP6WwrS.js +2311 -0
  19. package/dist/chunks/plugin-bkP6WwrS.js.map +1 -0
  20. package/dist/chunks/runtime-BH519j2t.js +294 -0
  21. package/dist/chunks/runtime-BH519j2t.js.map +1 -0
  22. package/dist/chunks/streaming-BlKlC3nw.js +427 -0
  23. package/dist/chunks/streaming-BlKlC3nw.js.map +1 -0
  24. package/dist/chunks/{testing-DueXbMo4.js → testing-BEW1nchS.js} +2161 -1562
  25. package/dist/chunks/testing-BEW1nchS.js.map +1 -0
  26. package/dist/chunks/{vite-DO70kZA6.js → vite-BADZA8Fl.js} +90 -87
  27. package/dist/chunks/vite-BADZA8Fl.js.map +1 -0
  28. package/dist/cli.d.ts +10 -2
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/cli.js +277 -87
  31. package/dist/cli.js.map +1 -1
  32. package/dist/config/index.js +2 -2
  33. package/dist/config/types.d.ts +14 -2
  34. package/dist/config/types.d.ts.map +1 -1
  35. package/dist/config/validate.d.ts.map +1 -1
  36. package/dist/data/cache.d.ts +5 -0
  37. package/dist/data/cache.d.ts.map +1 -1
  38. package/dist/data/index.d.ts +1 -1
  39. package/dist/data/index.d.ts.map +1 -1
  40. package/dist/data/testing.d.ts.map +1 -1
  41. package/dist/data/testing.js +101 -67
  42. package/dist/data/testing.js.map +1 -1
  43. package/dist/data/tiered.d.ts.map +1 -1
  44. package/dist/edge/index.js +27 -26
  45. package/dist/edge/index.js.map +1 -1
  46. package/dist/edge/stores.d.ts.map +1 -1
  47. package/dist/index.d.ts +4 -4
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +158 -135
  50. package/dist/index.js.map +1 -1
  51. package/dist/inspect/command.d.ts +28 -0
  52. package/dist/inspect/command.d.ts.map +1 -0
  53. package/dist/inspect/index.d.ts +9 -0
  54. package/dist/inspect/index.d.ts.map +1 -0
  55. package/dist/inspect/redact.d.ts +35 -0
  56. package/dist/inspect/redact.d.ts.map +1 -0
  57. package/dist/inspect/report.d.ts +24 -0
  58. package/dist/inspect/report.d.ts.map +1 -0
  59. package/dist/inspect/types.d.ts +102 -0
  60. package/dist/inspect/types.d.ts.map +1 -0
  61. package/dist/internal/testing.js +12 -12
  62. package/dist/kv/index.js +7 -6
  63. package/dist/kv/index.js.map +1 -1
  64. package/dist/kv/store.d.ts.map +1 -1
  65. package/dist/node/http.d.ts +7 -0
  66. package/dist/node/http.d.ts.map +1 -1
  67. package/dist/node/index.d.ts +2 -2
  68. package/dist/node/index.d.ts.map +1 -1
  69. package/dist/node/index.js +31 -28
  70. package/dist/node/index.js.map +1 -1
  71. package/dist/node/serve.d.ts +4 -0
  72. package/dist/node/serve.d.ts.map +1 -1
  73. package/dist/redis/index.js +29 -28
  74. package/dist/redis/index.js.map +1 -1
  75. package/dist/redis/store.d.ts.map +1 -1
  76. package/dist/route/index.d.ts +1 -0
  77. package/dist/route/index.d.ts.map +1 -1
  78. package/dist/route/mutation.d.ts.map +1 -1
  79. package/dist/route/serialization.d.ts +24 -0
  80. package/dist/route/serialization.d.ts.map +1 -0
  81. package/dist/route/webhook.d.ts +6 -1
  82. package/dist/route/webhook.d.ts.map +1 -1
  83. package/dist/router/manifest.d.ts.map +1 -1
  84. package/dist/security/audit.d.ts +2 -1
  85. package/dist/security/audit.d.ts.map +1 -1
  86. package/dist/security/authorization.d.ts +36 -0
  87. package/dist/security/authorization.d.ts.map +1 -0
  88. package/dist/security/csrf.d.ts.map +1 -1
  89. package/dist/security/env-startup.d.ts +0 -2
  90. package/dist/security/env-startup.d.ts.map +1 -1
  91. package/dist/security/env.d.ts +27 -8
  92. package/dist/security/env.d.ts.map +1 -1
  93. package/dist/security/exceptions.d.ts +29 -0
  94. package/dist/security/exceptions.d.ts.map +1 -0
  95. package/dist/security/finding-codes.d.ts +56 -0
  96. package/dist/security/finding-codes.d.ts.map +1 -0
  97. package/dist/security/index.d.ts +5 -2
  98. package/dist/security/index.d.ts.map +1 -1
  99. package/dist/security/policy.d.ts +2 -5
  100. package/dist/security/policy.d.ts.map +1 -1
  101. package/dist/security/report.d.ts +2 -1
  102. package/dist/security/report.d.ts.map +1 -1
  103. package/dist/security/request.d.ts +1 -0
  104. package/dist/security/request.d.ts.map +1 -1
  105. package/dist/security/types.d.ts +138 -5
  106. package/dist/security/types.d.ts.map +1 -1
  107. package/dist/security/upload.d.ts +3 -3
  108. package/dist/security/upload.d.ts.map +1 -1
  109. package/dist/security/verification.d.ts.map +1 -1
  110. package/dist/server/request-handler.d.ts +3 -2
  111. package/dist/server/request-handler.d.ts.map +1 -1
  112. package/dist/server-only/browser.d.ts +2 -0
  113. package/dist/server-only/browser.d.ts.map +1 -0
  114. package/dist/server-only/browser.js +4 -0
  115. package/dist/server-only/browser.js.map +1 -0
  116. package/dist/server-only/index.d.ts +2 -0
  117. package/dist/server-only/index.d.ts.map +1 -0
  118. package/dist/server-only/index.js +2 -0
  119. package/dist/server-only/index.js.map +1 -0
  120. package/dist/static/index.d.ts.map +1 -1
  121. package/dist/static/index.js +255 -255
  122. package/dist/static/index.js.map +1 -1
  123. package/dist/static/testing.d.ts +26 -0
  124. package/dist/static/testing.d.ts.map +1 -0
  125. package/dist/static/testing.js +76 -0
  126. package/dist/static/testing.js.map +1 -0
  127. package/dist/testing/index.d.ts +47 -0
  128. package/dist/testing/index.d.ts.map +1 -0
  129. package/dist/testing/index.js +99 -0
  130. package/dist/testing/index.js.map +1 -0
  131. package/dist/vercel/build.d.ts +31 -0
  132. package/dist/vercel/build.d.ts.map +1 -0
  133. package/dist/vercel/config.d.ts +15 -0
  134. package/dist/vercel/config.d.ts.map +1 -0
  135. package/dist/vercel/index.d.ts +7 -0
  136. package/dist/vercel/index.d.ts.map +1 -0
  137. package/dist/vercel/index.js +11 -0
  138. package/dist/vercel/index.js.map +1 -0
  139. package/dist/vercel/runtime.d.ts +29 -0
  140. package/dist/vercel/runtime.d.ts.map +1 -0
  141. package/dist/vite/index.d.ts +9 -4
  142. package/dist/vite/index.d.ts.map +1 -1
  143. package/dist/vite/index.js +28 -21
  144. package/dist/vite/plugin.d.ts +18 -2
  145. package/dist/vite/plugin.d.ts.map +1 -1
  146. package/dist/vite/policy-verification.d.ts +33 -2
  147. package/dist/vite/policy-verification.d.ts.map +1 -1
  148. package/dist/vite/route-audit.d.ts +14 -2
  149. package/dist/vite/route-audit.d.ts.map +1 -1
  150. package/dist/vite/server-only-boundary.d.ts +8 -0
  151. package/dist/vite/server-only-boundary.d.ts.map +1 -0
  152. package/package.json +18 -1
  153. package/dist/chunks/env-startup-DOBYWlZD.js +0 -257
  154. package/dist/chunks/env-startup-DOBYWlZD.js.map +0 -1
  155. package/dist/chunks/index-BVk9zWbe.js.map +0 -1
  156. package/dist/chunks/plugin-BQgMiHxa.js +0 -1827
  157. package/dist/chunks/plugin-BQgMiHxa.js.map +0 -1
  158. package/dist/chunks/streaming-BEbPN7Uk.js +0 -469
  159. package/dist/chunks/streaming-BEbPN7Uk.js.map +0 -1
  160. package/dist/chunks/testing-DueXbMo4.js.map +0 -1
  161. package/dist/chunks/vite-DO70kZA6.js.map +0 -1
@@ -0,0 +1,28 @@
1
+ import type { ResolvedDemiurgeConfig } from "../config/types";
2
+ /** The exit codes of the `demiurge inspect` command. */
3
+ export declare const INSPECT_EXIT_OK = 0;
4
+ export declare const INSPECT_EXIT_FINDINGS = 1;
5
+ export declare const INSPECT_EXIT_INVALID = 2;
6
+ export type InspectCommandInput = {
7
+ /** The arguments that follow the command name. */
8
+ arguments: readonly string[];
9
+ /** Reads the configuration of the application. */
10
+ loadConfig: () => Promise<ResolvedDemiurgeConfig>;
11
+ };
12
+ export type InspectCommandResult = {
13
+ /** The exit code of the command. */
14
+ exitCode: number;
15
+ /** The human summary that the command writes to standard error. */
16
+ stderr: string;
17
+ /** The JSON document that the command writes to standard output. */
18
+ stdout: string;
19
+ };
20
+ /**
21
+ * Runs the `demiurge inspect` command.
22
+ *
23
+ * The command reads the route tree, resolves the policy cascade, and returns
24
+ * one JSON document. The command loads no route module, so it runs no data
25
+ * loader and no mutation handler.
26
+ */
27
+ export declare function runInspectCommand(input: InspectCommandInput): Promise<InspectCommandResult>;
28
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/inspect/command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAe9D,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,MAAM,MAAM,mBAAmB,GAAG;IAChC,kDAAkD;IAClD,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,kDAAkD;IAClD,UAAU,EAAE,MAAM,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAmC/B"}
@@ -0,0 +1,9 @@
1
+ export { INSPECT_EXIT_FINDINGS, INSPECT_EXIT_INVALID, INSPECT_EXIT_OK, runInspectCommand, } from "./command";
2
+ export type { InspectCommandInput, InspectCommandResult, } from "./command";
3
+ export { redactSecrets, REDACTED_VALUE, serializeInspectionReport, } from "./redact";
4
+ export type { InspectionRedactionResult, InspectionSerializeOptions, } from "./redact";
5
+ export { countErrorFindings, createStaticPolicyReport, findSecretEnvKeys, formatStaticPolicyReportSummary, } from "./report";
6
+ export type { StaticPolicyReportOptions } from "./report";
7
+ export { INSPECTION_PROBLEM_VERSION, STATIC_POLICY_REPORT_VERSION, } from "./types";
8
+ export type { InspectionProblem, InspectionProblemCode, InspectionRedaction, InspectionRedactionReason, InspectionResolution, StaticPolicyReport, StaticPolicyReportSection, StaticRouteKind, StaticRouteReport, } from "./types";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inspect/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,aAAa,EACb,cAAc,EACd,yBAAyB,GAC1B,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,iBAAiB,EACjB,+BAA+B,GAChC,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EACL,0BAA0B,EAC1B,4BAA4B,GAC7B,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,EACzB,eAAe,EACf,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { InspectionRedaction } from "./types";
2
+ /**
3
+ * The text that replaces a removed value. A reader sees this marker in place
4
+ * of the value, and the `redactions` section names the field and the reason.
5
+ */
6
+ export declare const REDACTED_VALUE = "[redacted]";
7
+ export type InspectionSerializeOptions = {
8
+ /**
9
+ * The names of the environment variables that an `env.secret(...)`
10
+ * declaration produced.
11
+ */
12
+ secretEnvKeys?: Iterable<string>;
13
+ /** The indentation of the JSON output. The default value is 2. */
14
+ space?: number;
15
+ };
16
+ export type InspectionRedactionResult = {
17
+ /** The values that this module removed, sorted by path. */
18
+ redactions: InspectionRedaction[];
19
+ /** The report without the removed values. */
20
+ value: unknown;
21
+ };
22
+ /**
23
+ * Serializes a report and removes each secret value. This function is the one
24
+ * choke point of ADR 0019.
25
+ *
26
+ * The function writes the collected redactions into the `redactions` field of
27
+ * the report when the report declares that field.
28
+ */
29
+ export declare function serializeInspectionReport(report: unknown, options?: InspectionSerializeOptions): string;
30
+ /**
31
+ * Removes each secret value from a report and returns the removed names. The
32
+ * function does not change the report that the caller supplied.
33
+ */
34
+ export declare function redactSecrets(report: unknown, options?: InspectionSerializeOptions): InspectionRedactionResult;
35
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/inspect/redact.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EAEpB,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AACH,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;OAGG;IACH,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,2DAA2D;IAC3D,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AA+BF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,OAAO,EACf,OAAO,GAAE,0BAA+B,GACvC,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,OAAO,GAAE,0BAA+B,GACvC,yBAAyB,CAY3B"}
@@ -0,0 +1,24 @@
1
+ import type { EnvSchema } from "../security";
2
+ import { type DemiurgeVitePluginOptions } from "../vite/plugin";
3
+ import { type StaticPolicyReport } from "./types";
4
+ export type StaticPolicyReportOptions = {
5
+ /** The plugin options of the application. */
6
+ options?: DemiurgeVitePluginOptions;
7
+ /** The absolute path of the project root. */
8
+ root: string;
9
+ };
10
+ /**
11
+ * Builds the static inspection report of an application.
12
+ *
13
+ * The function reads the route tree and resolves the policy cascade through
14
+ * `inspectRoutePolicies`. It loads no route module, so it runs no data loader
15
+ * and no mutation handler.
16
+ */
17
+ export declare function createStaticPolicyReport(input: StaticPolicyReportOptions): Promise<StaticPolicyReport>;
18
+ /** Reads the names of the variables that `env.secret(...)` declared. */
19
+ export declare function findSecretEnvKeys(schema: EnvSchema | undefined): string[];
20
+ /** Counts the findings that carry error severity. */
21
+ export declare function countErrorFindings(report: StaticPolicyReport): number;
22
+ /** Writes the human summary that the command sends to standard error. */
23
+ export declare function formatStaticPolicyReportSummary(report: StaticPolicyReport): string;
24
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/inspect/report.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,yBAAyB,GAAG;IACtC,6CAA6C;IAC7C,OAAO,CAAC,EAAE,yBAAyB,CAAC;IACpC,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAiBF;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,kBAAkB,CAAC,CAqB7B;AAED,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,YAK9D;AAED,qDAAqD;AACrD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,UAG5D;AAED,yEAAyE;AACzE,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,kBAAkB,UAYzE"}
@@ -0,0 +1,102 @@
1
+ import type { HttpMethod } from "../route";
2
+ import type { DocumentCspState, RouteAccessState, StaticPolicyFinding } from "../vite/policy-verification";
3
+ /**
4
+ * The version of the static policy report. ADR 0019 raises this integer when
5
+ * the framework removes a field or changes the meaning of a field.
6
+ */
7
+ export declare const STATIC_POLICY_REPORT_VERSION = 1;
8
+ /**
9
+ * The version of the problem document that the inspect command writes when it
10
+ * cannot produce a report.
11
+ */
12
+ export declare const INSPECTION_PROBLEM_VERSION = 1;
13
+ /**
14
+ * The origin of one fact in a report. The value `static` states that the build
15
+ * knows the fact. The value `request` states that only a request supplies it.
16
+ */
17
+ export type InspectionResolution = "request" | "static";
18
+ /** The rule that removed one value from a report. */
19
+ export type InspectionRedactionReason =
20
+ /** A cookie holds the value. */
21
+ "cookie-value"
22
+ /** An `env.secret(...)` declaration produced the value. */
23
+ | "environment-secret"
24
+ /** A session record holds the value. */
25
+ | "session-record";
26
+ /** One value that the serializer removed from a report. */
27
+ export type InspectionRedaction = {
28
+ /** The name of the field that held the value. */
29
+ name: string;
30
+ /** The dotted path of the field inside the report. */
31
+ path: string;
32
+ /** The rule that removed the value. */
33
+ reason: InspectionRedactionReason;
34
+ };
35
+ /** The named sections of the static policy report. */
36
+ export type StaticPolicyReportSection = "findings" | "redactions" | "request" | "routes";
37
+ /** The kind of route file that the static pass read. */
38
+ export type StaticRouteKind = "attached" | "page" | "resource";
39
+ /**
40
+ * The statically readable description of one route file.
41
+ *
42
+ * Each declared field states what the file itself declares. The framework
43
+ * resolves the cascade of the route tree and reports the result of the
44
+ * cascade in the `findings` section.
45
+ */
46
+ export type StaticRouteReport = {
47
+ /** The access state that the route file declares. */
48
+ declaredAccess: RouteAccessState;
49
+ /** The Content-Security-Policy state that the route file declares. */
50
+ declaredDocumentCsp: DocumentCspState;
51
+ /** `true` when the route file declares a document policy. */
52
+ declaresDocumentPolicy: boolean;
53
+ /** The path of the route file, relative to the route directory. */
54
+ file: string;
55
+ /** The kind of the route file. */
56
+ kind: StaticRouteKind;
57
+ /** The HTTP methods that the route file exports. */
58
+ methods: HttpMethod[];
59
+ /** The route pattern. An attached file has no pattern. */
60
+ pattern?: string;
61
+ };
62
+ /**
63
+ * The report that `demiurge inspect` writes to standard output. Each section
64
+ * states the resolution of its facts in the `resolutions` record.
65
+ */
66
+ export type StaticPolicyReport = {
67
+ /** The static policy findings, sorted by file, export name, and code. */
68
+ findings: StaticPolicyFinding[];
69
+ /** The values that the serializer removed. */
70
+ redactions: InspectionRedaction[];
71
+ /** The names of the facts that only a request supplies. */
72
+ request: string[];
73
+ /** The resolution of each section of the report. */
74
+ resolutions: Record<StaticPolicyReportSection, InspectionResolution>;
75
+ /** Every route file under the route directory. */
76
+ routes: StaticRouteReport[];
77
+ /** The route directory, relative to the project root. */
78
+ routesDir: string;
79
+ /** The version of this report shape. */
80
+ version: number;
81
+ };
82
+ /** The reason that the inspect command could not produce a report. */
83
+ export type InspectionProblemCode =
84
+ /** The framework could not read the configuration. */
85
+ "configuration-unreadable"
86
+ /** The command received an invalid argument. */
87
+ | "invalid-argument";
88
+ /**
89
+ * The document that the inspect command writes when it cannot produce a
90
+ * report. The command then exits with code 2.
91
+ */
92
+ export type InspectionProblem = {
93
+ /** The stable code of the condition. */
94
+ code: InspectionProblemCode;
95
+ /** The description of the condition. */
96
+ detail: string;
97
+ /** The short name of the condition. */
98
+ title: string;
99
+ /** The version of this problem shape. */
100
+ version: number;
101
+ };
102
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/inspect/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,6BAA6B,CAAC;AAErC;;;GAGG;AACH,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAExD,qDAAqD;AACrD,MAAM,MAAM,yBAAyB;AACnC,gCAAgC;AAC9B,cAAc;AAChB,2DAA2D;GACzD,oBAAoB;AACtB,wCAAwC;GACtC,gBAAgB,CAAC;AAErB,2DAA2D;AAC3D,MAAM,MAAM,mBAAmB,GAAG;IAChC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,EAAE,yBAAyB,CAAC;CACnC,CAAC;AAEF,sDAAsD;AACtD,MAAM,MAAM,yBAAyB,GACjC,UAAU,GACV,YAAY,GACZ,SAAS,GACT,QAAQ,CAAC;AAEb,wDAAwD;AACxD,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,qDAAqD;IACrD,cAAc,EAAE,gBAAgB,CAAC;IACjC,sEAAsE;IACtE,mBAAmB,EAAE,gBAAgB,CAAC;IACtC,6DAA6D;IAC7D,sBAAsB,EAAE,OAAO,CAAC;IAChC,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,oDAAoD;IACpD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yEAAyE;IACzE,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,8CAA8C;IAC9C,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC,yBAAyB,EAAE,oBAAoB,CAAC,CAAC;IACrE,kDAAkD;IAClD,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,qBAAqB;AAC/B,sDAAsD;AACpD,0BAA0B;AAC5B,gDAAgD;GAC9C,kBAAkB,CAAC;AAEvB;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,wCAAwC;IACxC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC"}
@@ -1,23 +1,23 @@
1
- import { h as u, b as n, D as b, aM as i, w as _, q as r, aN as c, Q as R, P as d, p as h, aO as m, aP as p, aQ as P, aR as g, aS as f } from "../chunks/testing-DueXbMo4.js";
1
+ import { h as u, d as b, L as n, b9 as i, C as _, A as r, ba as c, Z as d, Y as h, z as m, bb as R, bc as g, bd as p, be as f, a as F } from "../chunks/testing-BEW1nchS.js";
2
2
  import "react-dom/server";
3
- import "../chunks/index-BVk9zWbe.js";
3
+ import "../chunks/index-LYve1rQp.js";
4
4
  import "react";
5
5
  import "react/jsx-runtime";
6
6
  export {
7
7
  u as handleRequestWithManifest,
8
- n as unstable_collectStaticRoutePaths,
9
- b as unstable_createRouteManifest,
8
+ b as unstable_collectStaticRoutePaths,
9
+ n as unstable_createRouteManifest,
10
10
  i as unstable_findPageMatch,
11
11
  _ as unstable_findRouteMatch,
12
12
  r as unstable_isAttachedFileForRoute,
13
13
  c as unstable_isLayoutForPage,
14
- R as unstable_loadErrorFallback,
15
- d as unstable_loadLoadingFallback,
16
- h as unstable_loadPageRoute,
17
- m as unstable_loadRoute,
18
- p as unstable_matchSegments,
19
- P as unstable_scoreRoute,
20
- g as unstable_splitPathname,
21
- f as unstable_toRouteSegments
14
+ d as unstable_loadErrorFallback,
15
+ h as unstable_loadLoadingFallback,
16
+ m as unstable_loadPageRoute,
17
+ R as unstable_loadRoute,
18
+ g as unstable_matchSegments,
19
+ p as unstable_scoreRoute,
20
+ f as unstable_splitPathname,
21
+ F as unstable_toRouteSegments
22
22
  };
23
23
  //# sourceMappingURL=testing.js.map
package/dist/kv/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { s as R, p as b } from "../chunks/session-store-ChModVcq.js";
1
+ import { s as A, p as R } from "../chunks/session-store-ChModVcq.js";
2
2
  const M = "demiurge:cache:", O = 1e3;
3
3
  function T(r) {
4
4
  if (!r.namespace || typeof r.namespace.get != "function")
@@ -38,6 +38,7 @@ function T(r) {
38
38
  await t.put(s(e), JSON.stringify(a), { expiration: p }), await y(e, a.tags, p);
39
39
  }
40
40
  return {
41
+ capabilities: { atomicity: "best-effort" },
41
42
  async acquireRefreshLease(e, a, l) {
42
43
  return await t.get(n(e)) ? !1 : (await t.put(n(e), a, {
43
44
  expiration: $(l)
@@ -130,7 +131,7 @@ function I(r) {
130
131
  throw new Error(
131
132
  "Demiurge KV session store requires atomic compare-and-swap operations. Adapt the provider transaction API to EdgeKvSessionNamespace.atomic."
132
133
  );
133
- const t = r.store, d = R(r.namespace), w = `${r.keyPrefix ?? D}${d}:`, f = (c) => w + c;
134
+ const t = r.store, d = A(r.namespace), w = `${r.keyPrefix ?? D}${d}:`, f = (c) => w + c;
134
135
  return {
135
136
  async create(c) {
136
137
  const s = P(c, 0);
@@ -150,7 +151,7 @@ function I(r) {
150
151
  return;
151
152
  const i = K(o);
152
153
  if (i) {
153
- if (A(i) <= s) {
154
+ if (b(i) <= s) {
154
155
  await t.atomic([{ expected: o, key: n }]);
155
156
  return;
156
157
  }
@@ -197,14 +198,14 @@ function x(r, t, d) {
197
198
  expected: t,
198
199
  key: r,
199
200
  write: {
200
- options: { expiration: Math.ceil(A(d) / 1e3) },
201
+ options: { expiration: Math.ceil(b(d) / 1e3) },
201
202
  value: JSON.stringify(d)
202
203
  }
203
204
  };
204
205
  }
205
206
  function K(r) {
206
207
  try {
207
- return b(JSON.parse(r));
208
+ return R(JSON.parse(r));
208
209
  } catch {
209
210
  return;
210
211
  }
@@ -212,7 +213,7 @@ function K(r) {
212
213
  function P(r, t) {
213
214
  return structuredClone({ ...r, version: t });
214
215
  }
215
- function A(r) {
216
+ function b(r) {
216
217
  return Math.min(r.expiresAt, r.idleExpiresAt ?? r.expiresAt);
217
218
  }
218
219
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/kv/store.ts","../../src/kv/rate-limit-store.ts","../../src/kv/session-store.ts"],"sourcesContent":["import type { CacheStore, CacheStoreEntry } from \"../data/cache\";\n\n// This type documents the interface an adapter FOR a KV store needs, not a\n// dependency on any vendor SDK. It is modeled closely on the binding API\n// Cloudflare Workers KV exposes, because that shape is the one most other\n// edge KV providers already copy. The application supplies its own\n// connected client matching this interface, the same way\n// `createRedisCacheStore` takes a connected `ioredis` client rather than\n// owning the connection. A Deno KV or Vercel Edge Config client can satisfy\n// this interface with a small wrapper even when its native method names\n// differ.\nexport type EdgeKvListKey = {\n name: string;\n};\n\nexport type EdgeKvListResult = {\n cursor?: string;\n keys: readonly EdgeKvListKey[];\n list_complete: boolean;\n};\n\nexport type EdgeKvPutOptions = {\n // Absolute expiration, in seconds since the Unix epoch. This store always\n // uses `expiration` rather than `expirationTtl` so an entry's lifetime\n // tracks its `staleUntil` timestamp exactly. Redis gets the same result\n // with `PEXPIREAT` instead of a relative `PEXPIRE`.\n expiration?: number;\n};\n\nexport type EdgeKvNamespace = {\n delete: (key: string) => Promise<void>;\n get: (key: string) => Promise<string | null>;\n list: (options?: {\n cursor?: string;\n limit?: number;\n prefix?: string;\n }) => Promise<EdgeKvListResult>;\n put: (key: string, value: string, options?: EdgeKvPutOptions) => Promise<void>;\n};\n\nexport type KvCacheStoreOptions = {\n // Every consumer of one KV namespace shares one keyspace. This prefix\n // scopes the store's entry, tag, and lease keys away from an\n // application's other use of the same namespace. Two Demiurge\n // deployments that share a namespace still need distinct prefixes, the\n // same way they need distinct cache namespaces.\n keyPrefix?: string;\n // The store issues operations on a client the application already\n // constructed and bound. Which provider, region, and namespace ID back\n // it is the application's decision, not this store's.\n namespace: EdgeKvNamespace;\n};\n\nconst defaultKeyPrefix = \"demiurge:cache:\";\nconst listPageLimit = 1000;\n\n// KV stores give up two things Redis's Lua scripts provided: cross-key\n// atomicity and a compare-and-swap primitive. Every write below is\n// therefore several sequential operations rather than one atomic script.\n//\n// Concretely, `set()`, `delete()`, and `publishRefresh()` each read the\n// previous entry, then write tag membership changes and the entry itself as\n// separate operations. A reader racing with one of these calls can briefly\n// see an entry whose tag memberships have not finished updating. It can\n// also see a tag membership entry whose backing cache entry is already\n// gone. `invalidateTags()` tolerates the latter case. It treats a\n// membership entry with no backing cache entry as already invalid and\n// simply removes it.\n//\n// `acquireRefreshLease()` and `publishRefresh()` implement single-writer\n// coordination with a get-then-write pattern, not a real compare-and-swap.\n// Two isolates that call `acquireRefreshLease()` within the same\n// observation window can both see no lease and both write one. The worst\n// case is redundant refresh work, the same failure mode\n// `staleWhileRevalidate` already tolerates when no coordination exists at\n// all. Do not rely on this store for exclusive-execution correctness\n// beyond that. The Redis store's atomic Lua scripts give a stronger\n// guarantee where that matters.\n//\n// Tag invalidation lists membership keys by prefix and deletes each\n// matching entry. `list()` on a real KV store is typically eventually\n// consistent. A membership entry written just before a matching `list()`\n// call may not appear on that call. A store that needs read-your-writes\n// tag invalidation should use the Redis store instead.\nexport function createKvCacheStore(options: KvCacheStoreOptions): CacheStore {\n if (!options.namespace || typeof options.namespace.get !== \"function\") {\n throw new Error(\n \"Demiurge KV cache store requires an EdgeKvNamespace. Pass a connected KV client matching the get/put/delete/list interface documented on EdgeKvNamespace.\",\n );\n }\n\n const kv = options.namespace;\n const prefix = options.keyPrefix ?? defaultKeyPrefix;\n const entryPrefix = `${prefix}entry:`;\n const tagPrefix = `${prefix}tag:`;\n const leasePrefix = `${prefix}lease:`;\n\n function entryKey(key: string) {\n return entryPrefix + key;\n }\n\n function leaseKey(key: string) {\n return leasePrefix + key;\n }\n\n function tagMemberKey(tagId: string, key: string) {\n return `${tagPrefix}${tagId}:${key}`;\n }\n\n async function readEntry(key: string): Promise<CacheStoreEntry | undefined> {\n const raw = await kv.get(entryKey(key));\n // TYPE-EVIDENCE: the stored raw value is JSON that the store serialized from a cache entry. The cast restores that shape.\n return raw ? (JSON.parse(raw) as CacheStoreEntry) : undefined;\n }\n\n async function removeTagMemberships(key: string, tags: readonly string[]) {\n await Promise.all(\n tags.map((tagId) => kv.delete(tagMemberKey(tagId, key))),\n );\n }\n\n async function addTagMemberships(\n key: string,\n tags: readonly string[],\n expiration: number | undefined,\n ) {\n await Promise.all(\n tags.map((tagId) =>\n kv.put(tagMemberKey(tagId, key), \"1\", { expiration })\n ),\n );\n }\n\n async function writeEntry(key: string, entry: CacheStoreEntry) {\n const previous = await readEntry(key);\n\n if (previous) {\n await removeTagMemberships(key, previous.tags);\n }\n\n const expiration = entryExpiration(entry);\n await kv.put(entryKey(key), JSON.stringify(entry), { expiration });\n await addTagMemberships(key, entry.tags, expiration);\n }\n\n return {\n async acquireRefreshLease(key, token, expiresAt) {\n const existing = await kv.get(leaseKey(key));\n\n if (existing) {\n return false;\n }\n\n await kv.put(leaseKey(key), token, {\n expiration: toExpirationSeconds(expiresAt),\n });\n\n return true;\n },\n async delete(key) {\n const previous = await readEntry(key);\n await kv.delete(leaseKey(key));\n\n if (!previous) {\n return false;\n }\n\n await removeTagMemberships(key, previous.tags);\n await kv.delete(entryKey(key));\n\n return true;\n },\n async get(key) {\n return await readEntry(key);\n },\n async invalidateTags(tags) {\n if (tags.length === 0) {\n return 0;\n }\n\n const seen = new Set<string>();\n let deleted = 0;\n\n for (const tagId of tags) {\n const membershipPrefix = tagMemberKey(tagId, \"\");\n let cursor: string | undefined;\n\n do {\n const page = await kv.list({\n cursor,\n limit: listPageLimit,\n prefix: membershipPrefix,\n });\n\n for (const listedKey of page.keys) {\n const rawKey = listedKey.name.slice(membershipPrefix.length);\n\n if (seen.has(rawKey)) {\n continue;\n }\n\n seen.add(rawKey);\n\n const entry = await readEntry(rawKey);\n\n if (!entry) {\n await kv.delete(listedKey.name);\n continue;\n }\n\n await removeTagMemberships(rawKey, entry.tags);\n await kv.delete(entryKey(rawKey));\n await kv.delete(leaseKey(rawKey));\n deleted += 1;\n }\n\n cursor = page.list_complete ? undefined : page.cursor;\n } while (cursor);\n }\n\n return deleted;\n },\n async publishRefresh(key, token, entry) {\n const owner = await kv.get(leaseKey(key));\n\n if (owner !== token) {\n return false;\n }\n\n await writeEntry(key, entry);\n await kv.delete(leaseKey(key));\n\n return true;\n },\n async releaseRefreshLease(key, token) {\n const owner = await kv.get(leaseKey(key));\n\n if (owner === token) {\n await kv.delete(leaseKey(key));\n }\n },\n async set(key, entry) {\n await writeEntry(key, entry);\n await kv.delete(leaseKey(key));\n },\n };\n}\n\nfunction entryExpiration(entry: CacheStoreEntry) {\n return entry.staleUntil === null\n ? undefined\n : toExpirationSeconds(entry.staleUntil);\n}\n\nfunction toExpirationSeconds(epochMilliseconds: number) {\n return Math.ceil(epochMilliseconds / 1000);\n}\n","import type { RateLimitStore } from \"../security/types\";\nimport type { EdgeKvNamespace } from \"./store\";\n\nexport type KvRateLimitStoreOptions = {\n // Every consumer of one KV namespace shares one keyspace. This prefix\n // scopes the store's counter keys away from an application's other use of\n // the same namespace, the same way `KvCacheStoreOptions.keyPrefix` scopes\n // `createKvCacheStore`'s keys.\n keyPrefix?: string;\n // The store issues operations on a client the application already\n // constructed and bound. Which provider, region, and namespace ID back\n // it is the application's decision, not this store's.\n namespace: EdgeKvNamespace;\n};\n\ntype KvRateLimitEntry = {\n count: number;\n resetAt: number;\n};\n\nconst defaultKeyPrefix = \"demiurge:rate-limit:\";\n\n// KV gives up the same thing here that `createKvCacheStore` gives up:\n// cross-operation atomicity. Plain KV has no compare-and-swap, so\n// `increment()` is necessarily a get-then-write, not a single atomic\n// operation. Two requests that call `increment()` within the same\n// observation window can both read the same prior count. Both then write\n// `count + 1`, so one increment is lost. The store can therefore undercount\n// under concurrent requests in that narrow window. It cannot overcount, and\n// it never blocks a request that should have been limited by more than one\n// request's worth of slack. Treat this as a soft limiter suitable for\n// coarse abuse protection, not an exact one. The Redis store's atomic Lua\n// script gives a stronger guarantee where that matters.\nexport function createKvRateLimitStore(\n options: KvRateLimitStoreOptions,\n): RateLimitStore {\n if (!options.namespace || typeof options.namespace.get !== \"function\") {\n throw new Error(\n \"Demiurge KV rate limit store requires an EdgeKvNamespace. Pass a connected KV client matching the get/put/delete/list interface documented on EdgeKvNamespace.\",\n );\n }\n\n const kv = options.namespace;\n const prefix = options.keyPrefix ?? defaultKeyPrefix;\n\n function counterKey(key: string) {\n return `${prefix}${key}`;\n }\n\n return {\n async increment(key, windowMs, now) {\n const storeKey = counterKey(key);\n const raw = await kv.get(storeKey);\n // TYPE-EVIDENCE: the stored raw value is JSON that the store serialized as a rate limit entry. The cast restores that shape.\n const existing = raw ? (JSON.parse(raw) as KvRateLimitEntry) : undefined;\n\n const next: KvRateLimitEntry = !existing || existing.resetAt <= now\n ? { count: 1, resetAt: now + windowMs }\n : { count: existing.count + 1, resetAt: existing.resetAt };\n\n await kv.put(storeKey, JSON.stringify(next), {\n expiration: toExpirationSeconds(next.resetAt),\n });\n\n return next;\n },\n };\n}\n\nfunction toExpirationSeconds(epochMilliseconds: number) {\n return Math.ceil(epochMilliseconds / 1000);\n}\n","import {\n parseSessionRecord,\n serializeSessionNamespace,\n type SessionData,\n type SessionNamespace,\n type SessionRecord,\n type SessionStore,\n type SessionStoreCandidate,\n} from \"../security/session-store\";\nimport type { EdgeKvNamespace, EdgeKvPutOptions } from \"./store\";\n\nexport type EdgeKvAtomicOperation = {\n expected: string | null;\n key: string;\n write?: {\n options?: EdgeKvPutOptions;\n value: string;\n };\n};\n\nexport type EdgeKvSessionNamespace = EdgeKvNamespace & {\n atomic: (operations: readonly EdgeKvAtomicOperation[]) => Promise<boolean>;\n};\n\nexport type KvSessionStoreOptions = {\n keyPrefix?: string;\n namespace: SessionNamespace;\n store: EdgeKvSessionNamespace;\n};\n\nconst defaultKeyPrefix = \"demiurge:session:\";\n\nexport function createKvSessionStore<\n TData extends SessionData = SessionData,\n>(options: KvSessionStoreOptions): SessionStore<TData> {\n if (!options.store || typeof options.store.get !== \"function\") {\n throw new Error(\n \"Demiurge KV session store requires an EdgeKvSessionNamespace. Pass an application-owned KV client as `store`.\",\n );\n }\n\n if (typeof options.store.atomic !== \"function\") {\n throw new Error(\n \"Demiurge KV session store requires atomic compare-and-swap operations. Adapt the provider transaction API to EdgeKvSessionNamespace.atomic.\",\n );\n }\n\n const store = options.store;\n const namespace = serializeSessionNamespace(options.namespace);\n const prefix = `${options.keyPrefix ?? defaultKeyPrefix}${namespace}:`;\n const key = (id: string) => prefix + id;\n\n return {\n async create(candidate) {\n const record = toRecord(candidate, 0);\n\n try {\n const stored = await store.atomic([putOperation(key(record.id), null, record)]);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n async destroy(id) {\n const storageKey = key(id);\n const current = await store.get(storageKey);\n\n if (current === null) {\n return false;\n }\n\n return await store.atomic([{ expected: current, key: storageKey }]);\n },\n async read(id, now) {\n const storageKey = key(id);\n const raw = await store.get(storageKey);\n\n if (raw === null) {\n return undefined;\n }\n\n const record = parseRecord<TData>(raw);\n\n if (!record) {\n return undefined;\n }\n\n if (storeExpiration(record) <= now) {\n await store.atomic([{ expected: raw, key: storageKey }]);\n return undefined;\n }\n\n return record;\n },\n async rotate(currentId, candidate, expectedVersion) {\n const currentKey = key(currentId);\n const nextKey = key(candidate.id);\n const [current, next] = await Promise.all([\n store.get(currentKey),\n currentKey === nextKey ? Promise.resolve(null) : store.get(nextKey),\n ]);\n\n if (!current || (nextKey !== currentKey && next !== null)) {\n return { status: \"conflict\" };\n }\n\n const currentRecord = parseRecord<TData>(current);\n\n if (!currentRecord || currentRecord.version !== expectedVersion) {\n return { status: \"conflict\" };\n }\n\n const record = toRecord(candidate, expectedVersion + 1);\n const operations = currentKey === nextKey\n ? [putOperation(currentKey, current, record)]\n : [\n { expected: current, key: currentKey },\n putOperation(nextKey, null, record),\n ];\n\n try {\n const stored = await store.atomic(operations);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n async update(candidate, expectedVersion) {\n const storageKey = key(candidate.id);\n const current = await store.get(storageKey);\n\n const currentRecord = current ? parseRecord<TData>(current) : undefined;\n\n if (!currentRecord || currentRecord.version !== expectedVersion) {\n return { status: \"conflict\" };\n }\n\n const record = toRecord(candidate, expectedVersion + 1);\n\n try {\n const stored = await store.atomic([\n putOperation(storageKey, current, record),\n ]);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n };\n}\n\nfunction putOperation(\n key: string,\n expected: string | null,\n record: SessionRecord,\n): EdgeKvAtomicOperation {\n return {\n expected,\n key,\n write: {\n options: { expiration: Math.ceil(storeExpiration(record) / 1000) },\n value: JSON.stringify(record),\n },\n };\n}\n\nfunction parseRecord<TData extends SessionData>(\n raw: string,\n): SessionRecord<TData> | undefined {\n try {\n return parseSessionRecord<TData>(JSON.parse(raw));\n } catch {\n return undefined;\n }\n}\n\nfunction toRecord<TData extends SessionData>(\n candidate: SessionStoreCandidate<TData>,\n version: number,\n): SessionRecord<TData> {\n return structuredClone({ ...candidate, version });\n}\n\nfunction storeExpiration(record: SessionRecord) {\n return Math.min(record.expiresAt, record.idleExpiresAt ?? record.expiresAt);\n}\n"],"names":["defaultKeyPrefix","listPageLimit","createKvCacheStore","options","kv","prefix","entryPrefix","tagPrefix","leasePrefix","entryKey","key","leaseKey","tagMemberKey","tagId","readEntry","raw","removeTagMemberships","tags","addTagMemberships","expiration","writeEntry","entry","previous","entryExpiration","token","expiresAt","toExpirationSeconds","seen","deleted","membershipPrefix","cursor","page","listedKey","rawKey","epochMilliseconds","createKvRateLimitStore","counterKey","windowMs","now","storeKey","existing","next","createKvSessionStore","store","namespace","serializeSessionNamespace","id","candidate","record","toRecord","putOperation","storageKey","current","parseRecord","storeExpiration","currentId","expectedVersion","currentKey","nextKey","currentRecord","operations","expected","parseSessionRecord","version"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/kv/store.ts","../../src/kv/rate-limit-store.ts","../../src/kv/session-store.ts"],"sourcesContent":["import type { CacheStore, CacheStoreEntry } from \"../data/cache\";\n\n// This type documents the interface an adapter FOR a KV store needs, not a\n// dependency on any vendor SDK. It is modeled closely on the binding API\n// Cloudflare Workers KV exposes, because that shape is the one most other\n// edge KV providers already copy. The application supplies its own\n// connected client matching this interface, the same way\n// `createRedisCacheStore` takes a connected `ioredis` client rather than\n// owning the connection. A Deno KV or Vercel Edge Config client can satisfy\n// this interface with a small wrapper even when its native method names\n// differ.\nexport type EdgeKvListKey = {\n name: string;\n};\n\nexport type EdgeKvListResult = {\n cursor?: string;\n keys: readonly EdgeKvListKey[];\n list_complete: boolean;\n};\n\nexport type EdgeKvPutOptions = {\n // Absolute expiration, in seconds since the Unix epoch. This store always\n // uses `expiration` rather than `expirationTtl` so an entry's lifetime\n // tracks its `staleUntil` timestamp exactly. Redis gets the same result\n // with `PEXPIREAT` instead of a relative `PEXPIRE`.\n expiration?: number;\n};\n\nexport type EdgeKvNamespace = {\n delete: (key: string) => Promise<void>;\n get: (key: string) => Promise<string | null>;\n list: (options?: {\n cursor?: string;\n limit?: number;\n prefix?: string;\n }) => Promise<EdgeKvListResult>;\n put: (key: string, value: string, options?: EdgeKvPutOptions) => Promise<void>;\n};\n\nexport type KvCacheStoreOptions = {\n // Every consumer of one KV namespace shares one keyspace. This prefix\n // scopes the store's entry, tag, and lease keys away from an\n // application's other use of the same namespace. Two Demiurge\n // deployments that share a namespace still need distinct prefixes, the\n // same way they need distinct cache namespaces.\n keyPrefix?: string;\n // The store issues operations on a client the application already\n // constructed and bound. Which provider, region, and namespace ID back\n // it is the application's decision, not this store's.\n namespace: EdgeKvNamespace;\n};\n\nconst defaultKeyPrefix = \"demiurge:cache:\";\nconst listPageLimit = 1000;\n\n// KV stores give up two things Redis's Lua scripts provided: cross-key\n// atomicity and a compare-and-swap primitive. Every write below is\n// therefore several sequential operations rather than one atomic script.\n//\n// Concretely, `set()`, `delete()`, and `publishRefresh()` each read the\n// previous entry, then write tag membership changes and the entry itself as\n// separate operations. A reader racing with one of these calls can briefly\n// see an entry whose tag memberships have not finished updating. It can\n// also see a tag membership entry whose backing cache entry is already\n// gone. `invalidateTags()` tolerates the latter case. It treats a\n// membership entry with no backing cache entry as already invalid and\n// simply removes it.\n//\n// `acquireRefreshLease()` and `publishRefresh()` implement single-writer\n// coordination with a get-then-write pattern, not a real compare-and-swap.\n// Two isolates that call `acquireRefreshLease()` within the same\n// observation window can both see no lease and both write one. The worst\n// case is redundant refresh work, the same failure mode\n// `staleWhileRevalidate` already tolerates when no coordination exists at\n// all. Do not rely on this store for exclusive-execution correctness\n// beyond that. The Redis store's atomic Lua scripts give a stronger\n// guarantee where that matters.\n//\n// Tag invalidation lists membership keys by prefix and deletes each\n// matching entry. `list()` on a real KV store is typically eventually\n// consistent. A membership entry written just before a matching `list()`\n// call may not appear on that call. A store that needs read-your-writes\n// tag invalidation should use the Redis store instead.\nexport function createKvCacheStore(options: KvCacheStoreOptions): CacheStore {\n if (!options.namespace || typeof options.namespace.get !== \"function\") {\n throw new Error(\n \"Demiurge KV cache store requires an EdgeKvNamespace. Pass a connected KV client matching the get/put/delete/list interface documented on EdgeKvNamespace.\",\n );\n }\n\n const kv = options.namespace;\n const prefix = options.keyPrefix ?? defaultKeyPrefix;\n const entryPrefix = `${prefix}entry:`;\n const tagPrefix = `${prefix}tag:`;\n const leasePrefix = `${prefix}lease:`;\n\n function entryKey(key: string) {\n return entryPrefix + key;\n }\n\n function leaseKey(key: string) {\n return leasePrefix + key;\n }\n\n function tagMemberKey(tagId: string, key: string) {\n return `${tagPrefix}${tagId}:${key}`;\n }\n\n async function readEntry(key: string): Promise<CacheStoreEntry | undefined> {\n const raw = await kv.get(entryKey(key));\n // TYPE-EVIDENCE: the stored raw value is JSON that the store serialized from a cache entry. The cast restores that shape.\n return raw ? (JSON.parse(raw) as CacheStoreEntry) : undefined;\n }\n\n async function removeTagMemberships(key: string, tags: readonly string[]) {\n await Promise.all(\n tags.map((tagId) => kv.delete(tagMemberKey(tagId, key))),\n );\n }\n\n async function addTagMemberships(\n key: string,\n tags: readonly string[],\n expiration: number | undefined,\n ) {\n await Promise.all(\n tags.map((tagId) =>\n kv.put(tagMemberKey(tagId, key), \"1\", { expiration })\n ),\n );\n }\n\n async function writeEntry(key: string, entry: CacheStoreEntry) {\n const previous = await readEntry(key);\n\n if (previous) {\n await removeTagMemberships(key, previous.tags);\n }\n\n const expiration = entryExpiration(entry);\n await kv.put(entryKey(key), JSON.stringify(entry), { expiration });\n await addTagMemberships(key, entry.tags, expiration);\n }\n\n return {\n capabilities: { atomicity: \"best-effort\" },\n async acquireRefreshLease(key, token, expiresAt) {\n const existing = await kv.get(leaseKey(key));\n\n if (existing) {\n return false;\n }\n\n await kv.put(leaseKey(key), token, {\n expiration: toExpirationSeconds(expiresAt),\n });\n\n return true;\n },\n async delete(key) {\n const previous = await readEntry(key);\n await kv.delete(leaseKey(key));\n\n if (!previous) {\n return false;\n }\n\n await removeTagMemberships(key, previous.tags);\n await kv.delete(entryKey(key));\n\n return true;\n },\n async get(key) {\n return await readEntry(key);\n },\n async invalidateTags(tags) {\n if (tags.length === 0) {\n return 0;\n }\n\n const seen = new Set<string>();\n let deleted = 0;\n\n for (const tagId of tags) {\n const membershipPrefix = tagMemberKey(tagId, \"\");\n let cursor: string | undefined;\n\n do {\n const page = await kv.list({\n cursor,\n limit: listPageLimit,\n prefix: membershipPrefix,\n });\n\n for (const listedKey of page.keys) {\n const rawKey = listedKey.name.slice(membershipPrefix.length);\n\n if (seen.has(rawKey)) {\n continue;\n }\n\n seen.add(rawKey);\n\n const entry = await readEntry(rawKey);\n\n if (!entry) {\n await kv.delete(listedKey.name);\n continue;\n }\n\n await removeTagMemberships(rawKey, entry.tags);\n await kv.delete(entryKey(rawKey));\n await kv.delete(leaseKey(rawKey));\n deleted += 1;\n }\n\n cursor = page.list_complete ? undefined : page.cursor;\n } while (cursor);\n }\n\n return deleted;\n },\n async publishRefresh(key, token, entry) {\n const owner = await kv.get(leaseKey(key));\n\n if (owner !== token) {\n return false;\n }\n\n await writeEntry(key, entry);\n await kv.delete(leaseKey(key));\n\n return true;\n },\n async releaseRefreshLease(key, token) {\n const owner = await kv.get(leaseKey(key));\n\n if (owner === token) {\n await kv.delete(leaseKey(key));\n }\n },\n async set(key, entry) {\n await writeEntry(key, entry);\n await kv.delete(leaseKey(key));\n },\n };\n}\n\nfunction entryExpiration(entry: CacheStoreEntry) {\n return entry.staleUntil === null\n ? undefined\n : toExpirationSeconds(entry.staleUntil);\n}\n\nfunction toExpirationSeconds(epochMilliseconds: number) {\n return Math.ceil(epochMilliseconds / 1000);\n}\n","import type { RateLimitStore } from \"../security/types\";\nimport type { EdgeKvNamespace } from \"./store\";\n\nexport type KvRateLimitStoreOptions = {\n // Every consumer of one KV namespace shares one keyspace. This prefix\n // scopes the store's counter keys away from an application's other use of\n // the same namespace, the same way `KvCacheStoreOptions.keyPrefix` scopes\n // `createKvCacheStore`'s keys.\n keyPrefix?: string;\n // The store issues operations on a client the application already\n // constructed and bound. Which provider, region, and namespace ID back\n // it is the application's decision, not this store's.\n namespace: EdgeKvNamespace;\n};\n\ntype KvRateLimitEntry = {\n count: number;\n resetAt: number;\n};\n\nconst defaultKeyPrefix = \"demiurge:rate-limit:\";\n\n// KV gives up the same thing here that `createKvCacheStore` gives up:\n// cross-operation atomicity. Plain KV has no compare-and-swap, so\n// `increment()` is necessarily a get-then-write, not a single atomic\n// operation. Two requests that call `increment()` within the same\n// observation window can both read the same prior count. Both then write\n// `count + 1`, so one increment is lost. The store can therefore undercount\n// under concurrent requests in that narrow window. It cannot overcount, and\n// it never blocks a request that should have been limited by more than one\n// request's worth of slack. Treat this as a soft limiter suitable for\n// coarse abuse protection, not an exact one. The Redis store's atomic Lua\n// script gives a stronger guarantee where that matters.\nexport function createKvRateLimitStore(\n options: KvRateLimitStoreOptions,\n): RateLimitStore {\n if (!options.namespace || typeof options.namespace.get !== \"function\") {\n throw new Error(\n \"Demiurge KV rate limit store requires an EdgeKvNamespace. Pass a connected KV client matching the get/put/delete/list interface documented on EdgeKvNamespace.\",\n );\n }\n\n const kv = options.namespace;\n const prefix = options.keyPrefix ?? defaultKeyPrefix;\n\n function counterKey(key: string) {\n return `${prefix}${key}`;\n }\n\n return {\n async increment(key, windowMs, now) {\n const storeKey = counterKey(key);\n const raw = await kv.get(storeKey);\n // TYPE-EVIDENCE: the stored raw value is JSON that the store serialized as a rate limit entry. The cast restores that shape.\n const existing = raw ? (JSON.parse(raw) as KvRateLimitEntry) : undefined;\n\n const next: KvRateLimitEntry = !existing || existing.resetAt <= now\n ? { count: 1, resetAt: now + windowMs }\n : { count: existing.count + 1, resetAt: existing.resetAt };\n\n await kv.put(storeKey, JSON.stringify(next), {\n expiration: toExpirationSeconds(next.resetAt),\n });\n\n return next;\n },\n };\n}\n\nfunction toExpirationSeconds(epochMilliseconds: number) {\n return Math.ceil(epochMilliseconds / 1000);\n}\n","import {\n parseSessionRecord,\n serializeSessionNamespace,\n type SessionData,\n type SessionNamespace,\n type SessionRecord,\n type SessionStore,\n type SessionStoreCandidate,\n} from \"../security/session-store\";\nimport type { EdgeKvNamespace, EdgeKvPutOptions } from \"./store\";\n\nexport type EdgeKvAtomicOperation = {\n expected: string | null;\n key: string;\n write?: {\n options?: EdgeKvPutOptions;\n value: string;\n };\n};\n\nexport type EdgeKvSessionNamespace = EdgeKvNamespace & {\n atomic: (operations: readonly EdgeKvAtomicOperation[]) => Promise<boolean>;\n};\n\nexport type KvSessionStoreOptions = {\n keyPrefix?: string;\n namespace: SessionNamespace;\n store: EdgeKvSessionNamespace;\n};\n\nconst defaultKeyPrefix = \"demiurge:session:\";\n\nexport function createKvSessionStore<\n TData extends SessionData = SessionData,\n>(options: KvSessionStoreOptions): SessionStore<TData> {\n if (!options.store || typeof options.store.get !== \"function\") {\n throw new Error(\n \"Demiurge KV session store requires an EdgeKvSessionNamespace. Pass an application-owned KV client as `store`.\",\n );\n }\n\n if (typeof options.store.atomic !== \"function\") {\n throw new Error(\n \"Demiurge KV session store requires atomic compare-and-swap operations. Adapt the provider transaction API to EdgeKvSessionNamespace.atomic.\",\n );\n }\n\n const store = options.store;\n const namespace = serializeSessionNamespace(options.namespace);\n const prefix = `${options.keyPrefix ?? defaultKeyPrefix}${namespace}:`;\n const key = (id: string) => prefix + id;\n\n return {\n async create(candidate) {\n const record = toRecord(candidate, 0);\n\n try {\n const stored = await store.atomic([putOperation(key(record.id), null, record)]);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n async destroy(id) {\n const storageKey = key(id);\n const current = await store.get(storageKey);\n\n if (current === null) {\n return false;\n }\n\n return await store.atomic([{ expected: current, key: storageKey }]);\n },\n async read(id, now) {\n const storageKey = key(id);\n const raw = await store.get(storageKey);\n\n if (raw === null) {\n return undefined;\n }\n\n const record = parseRecord<TData>(raw);\n\n if (!record) {\n return undefined;\n }\n\n if (storeExpiration(record) <= now) {\n await store.atomic([{ expected: raw, key: storageKey }]);\n return undefined;\n }\n\n return record;\n },\n async rotate(currentId, candidate, expectedVersion) {\n const currentKey = key(currentId);\n const nextKey = key(candidate.id);\n const [current, next] = await Promise.all([\n store.get(currentKey),\n currentKey === nextKey ? Promise.resolve(null) : store.get(nextKey),\n ]);\n\n if (!current || (nextKey !== currentKey && next !== null)) {\n return { status: \"conflict\" };\n }\n\n const currentRecord = parseRecord<TData>(current);\n\n if (!currentRecord || currentRecord.version !== expectedVersion) {\n return { status: \"conflict\" };\n }\n\n const record = toRecord(candidate, expectedVersion + 1);\n const operations = currentKey === nextKey\n ? [putOperation(currentKey, current, record)]\n : [\n { expected: current, key: currentKey },\n putOperation(nextKey, null, record),\n ];\n\n try {\n const stored = await store.atomic(operations);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n async update(candidate, expectedVersion) {\n const storageKey = key(candidate.id);\n const current = await store.get(storageKey);\n\n const currentRecord = current ? parseRecord<TData>(current) : undefined;\n\n if (!currentRecord || currentRecord.version !== expectedVersion) {\n return { status: \"conflict\" };\n }\n\n const record = toRecord(candidate, expectedVersion + 1);\n\n try {\n const stored = await store.atomic([\n putOperation(storageKey, current, record),\n ]);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n };\n}\n\nfunction putOperation(\n key: string,\n expected: string | null,\n record: SessionRecord,\n): EdgeKvAtomicOperation {\n return {\n expected,\n key,\n write: {\n options: { expiration: Math.ceil(storeExpiration(record) / 1000) },\n value: JSON.stringify(record),\n },\n };\n}\n\nfunction parseRecord<TData extends SessionData>(\n raw: string,\n): SessionRecord<TData> | undefined {\n try {\n return parseSessionRecord<TData>(JSON.parse(raw));\n } catch {\n return undefined;\n }\n}\n\nfunction toRecord<TData extends SessionData>(\n candidate: SessionStoreCandidate<TData>,\n version: number,\n): SessionRecord<TData> {\n return structuredClone({ ...candidate, version });\n}\n\nfunction storeExpiration(record: SessionRecord) {\n return Math.min(record.expiresAt, record.idleExpiresAt ?? record.expiresAt);\n}\n"],"names":["defaultKeyPrefix","listPageLimit","createKvCacheStore","options","kv","prefix","entryPrefix","tagPrefix","leasePrefix","entryKey","key","leaseKey","tagMemberKey","tagId","readEntry","raw","removeTagMemberships","tags","addTagMemberships","expiration","writeEntry","entry","previous","entryExpiration","token","expiresAt","toExpirationSeconds","seen","deleted","membershipPrefix","cursor","page","listedKey","rawKey","epochMilliseconds","createKvRateLimitStore","counterKey","windowMs","now","storeKey","existing","next","createKvSessionStore","store","namespace","serializeSessionNamespace","id","candidate","record","toRecord","putOperation","storageKey","current","parseRecord","storeExpiration","currentId","expectedVersion","currentKey","nextKey","currentRecord","operations","expected","parseSessionRecord","version"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/kv/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,eAAe,CAAC;AAWjE,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAK7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAMhC,SAAS,CAAC,EAAE,MAAM,CAAC;IAInB,SAAS,EAAE,eAAe,CAAC;CAC5B,CAAC;AAiCF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,UAAU,CAkK3E"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/kv/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,eAAe,CAAC;AAWjE,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAK7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAMhC,SAAS,CAAC,EAAE,MAAM,CAAC;IAInB,SAAS,EAAE,eAAe,CAAC;CAC5B,CAAC;AAiCF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,UAAU,CAmK3E"}
@@ -5,6 +5,9 @@ export type NodeOriginPolicy = {
5
5
  trustProxy?: TrustProxy;
6
6
  };
7
7
  export type ToWebRequestOptions = NodeOriginPolicy & {
8
+ body?: BodyInit | null;
9
+ clientIp?: string;
10
+ scheme?: HttpScheme;
8
11
  signal?: AbortSignal;
9
12
  };
10
13
  export type TrustProxy = false | {
@@ -20,6 +23,10 @@ export declare class UntrustedHostError extends Error {
20
23
  host: string;
21
24
  constructor(host: string);
22
25
  }
26
+ export declare class ConsumedRequestBodyError extends Error {
27
+ method: string;
28
+ constructor(method: string);
29
+ }
23
30
  export declare function validateNodeOriginPolicy(policy: NodeOriginPolicy): void;
24
31
  export declare function toWebRequest(request: IncomingMessage, options: ToWebRequestOptions): Request;
25
32
  export declare function writeNotImplemented(serverResponse: ServerResponse): void;
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/node/http.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAGnB,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG;IACnD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB,KAAK,GACL;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAclC,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,MAAM,EAAE,MAAM,CAAC;gBAEH,MAAM,EAAE,MAAM;CAK3B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM;CAKzB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,QA+BhE;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,WA2C7B;AAiOD,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,QAIjE;AAED,wBAAsB,gBAAgB,CACpC,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE,QAAQ,iBAiDtB;AAsBD,wBAAgB,SAAS,CAAC,OAAO,EAAE,mBAAmB,WAiBrD"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/node/http.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAGnB,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG;IAGnD,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAGvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB,KAAK,GACL;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAclC,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,MAAM,EAAE,MAAM,CAAC;gBAEH,MAAM,EAAE,MAAM;CAK3B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM;CAKzB;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,MAAM,EAAE,MAAM,CAAC;gBAEH,MAAM,EAAE,MAAM;CAU3B;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,QA+BhE;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,WA0E7B;AAwZD,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,QAIjE;AAED,wBAAsB,gBAAgB,CACpC,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE,QAAQ,iBAiDtB;AAsBD,wBAAgB,SAAS,CAAC,OAAO,EAAE,mBAAmB,WAiBrD"}
@@ -1,14 +1,14 @@
1
1
  import type { IncomingMessage, Server, ServerResponse } from "node:http";
2
2
  import type { RequestHandler } from "../server";
3
3
  import type { StaticFileHandler, StaticFileHandlerOptions } from "./static";
4
- export { UntrustedHostError, UnsupportedMethodError, toHeaders, toWebRequest, validateNodeOriginPolicy, writeWebResponse, } from "./http";
4
+ export { ConsumedRequestBodyError, UntrustedHostError, UnsupportedMethodError, toHeaders, toWebRequest, validateNodeOriginPolicy, writeWebResponse, } from "./http";
5
5
  export { createFontAssetHandler } from "./font";
6
6
  export type { FontAssetHandlerOptions } from "./font";
7
7
  export { createImageOptimizer } from "./image";
8
8
  export type { ImageOptimizerFetch, ImageOptimizerOptions, } from "./image";
9
9
  export { createStaticFileHandler } from "./static";
10
10
  export { defaultNodeBuildClientDir, defaultNodeBuildReadyPath, serveNodeBuild, } from "./serve";
11
- export type { NodeBuildContext, NodeBuildPageOptions, ServeNodeBuildEnvironment, ServeNodeBuildOptions, } from "./serve";
11
+ export type { NodeBuildContext, NodeBuildPageOptions, NodeBuildRuntime, ServeNodeBuildEnvironment, ServeNodeBuildOptions, } from "./serve";
12
12
  export { renderNodePageResponse, renderStreamingPageResponse, } from "./streaming";
13
13
  export type { HttpScheme, NodeOriginPolicy, ToWebRequestOptions, TrustProxy, } from "./http";
14
14
  export type { StaticFileHandler, StaticFileHandlerOptions } from "./static";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAUhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,SAAS,EACT,YAAY,EACZ,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AAChD,YAAY,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,YAAY,EACV,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,cAAc,GACf,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,GACX,MAAM,QAAQ,CAAC;AAChB,YAAY,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAO5E,eAAO,MAAM,WAAW,sBAStB,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAChC,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,KACrB,IAAI,CAAC;AAEV,MAAM,MAAM,0BAA0B,GAAG,OAAO,QAAQ,EAAE,gBAAgB,GAAG;IAC3E,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7D,MAAM,CAAC,EAAE,iBAAiB,GAAG,wBAAwB,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,CAAC;AACtD,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAE/D,MAAM,MAAM,2BAA2B,GAAG;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IACjD,OAAO,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,0BAA0B,GAAG;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,2BAA2B,CAAC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAChC,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,eAAO,MAAM,yBAAyB;;;;CAIC,CAAC;AAIxC,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,mBAAmB,CAsDrB;AAoCD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,iBAAiB,GACzB,UAAU,CA+BZ"}