@substrat-run/control-plane-api 0.148.0 → 0.149.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 (67) hide show
  1. package/dist/api.d.ts +19 -11
  2. package/dist/api.d.ts.map +1 -1
  3. package/dist/api.js +321 -93
  4. package/dist/api.js.map +1 -1
  5. package/dist/attachment-sweep.d.ts +29 -0
  6. package/dist/attachment-sweep.d.ts.map +1 -0
  7. package/dist/attachment-sweep.js +66 -0
  8. package/dist/attachment-sweep.js.map +1 -0
  9. package/dist/browser.d.ts +3 -1
  10. package/dist/browser.d.ts.map +1 -1
  11. package/dist/browser.js +3 -0
  12. package/dist/browser.js.map +1 -1
  13. package/dist/cf-observability.d.ts.map +1 -1
  14. package/dist/cf-observability.js +3 -1
  15. package/dist/cf-observability.js.map +1 -1
  16. package/dist/deploy.d.ts +25 -11
  17. package/dist/deploy.d.ts.map +1 -1
  18. package/dist/deploy.js +32 -17
  19. package/dist/deploy.js.map +1 -1
  20. package/dist/index.d.ts +9 -7
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +6 -4
  23. package/dist/index.js.map +1 -1
  24. package/dist/observability.d.ts +28 -0
  25. package/dist/observability.d.ts.map +1 -1
  26. package/dist/observability.js +35 -0
  27. package/dist/observability.js.map +1 -1
  28. package/dist/platform-entry.generated.d.ts +1 -1
  29. package/dist/platform-entry.generated.d.ts.map +1 -1
  30. package/dist/platform-entry.generated.js +2 -2
  31. package/dist/platform-entry.generated.js.map +1 -1
  32. package/dist/platform-scope-stores.d.ts +12 -12
  33. package/dist/platform-scope-stores.d.ts.map +1 -1
  34. package/dist/platform-scope-stores.js +21 -21
  35. package/dist/platform-scope-stores.js.map +1 -1
  36. package/dist/scope-copy-cleanup.d.ts +1 -1
  37. package/dist/scope-copy-cleanup.d.ts.map +1 -1
  38. package/dist/scope-copy-cleanup.js.map +1 -1
  39. package/dist/staff-client.d.ts +10 -2
  40. package/dist/staff-client.d.ts.map +1 -1
  41. package/dist/staff-client.js +6 -1
  42. package/dist/staff-client.js.map +1 -1
  43. package/dist/store-entry.d.ts +19 -19
  44. package/dist/store-entry.d.ts.map +1 -1
  45. package/dist/store-entry.generated.d.ts +16 -16
  46. package/dist/store-entry.generated.d.ts.map +1 -1
  47. package/dist/store-entry.generated.js +19 -19
  48. package/dist/store-entry.generated.js.map +1 -1
  49. package/dist/store-entry.js +43 -43
  50. package/dist/store-entry.js.map +1 -1
  51. package/dist/store-rebuild.d.ts +142 -0
  52. package/dist/store-rebuild.d.ts.map +1 -0
  53. package/dist/store-rebuild.js +223 -0
  54. package/dist/store-rebuild.js.map +1 -0
  55. package/dist/store-releases.d.ts +60 -49
  56. package/dist/store-releases.d.ts.map +1 -1
  57. package/dist/store-releases.js +134 -100
  58. package/dist/store-releases.js.map +1 -1
  59. package/dist/store-script.d.ts +37 -19
  60. package/dist/store-script.d.ts.map +1 -1
  61. package/dist/store-script.js +38 -23
  62. package/dist/store-script.js.map +1 -1
  63. package/dist/vertical-client.d.ts +29 -2
  64. package/dist/vertical-client.d.ts.map +1 -1
  65. package/dist/vertical-client.js +48 -2
  66. package/dist/vertical-client.js.map +1 -1
  67. package/package.json +6 -6
@@ -1,22 +1,22 @@
1
1
  /**
2
2
  * The store script's entry (#2289), and the names it shares with the uploader
3
- * (store-entry/names.ts). The entry is uploaded as the script's main module. The kind
4
- * module replaces `__SUBSTRAT_STORE_KIND__` and its class name replaces
3
+ * (store-entry/names.ts). The entry is uploaded as the script's main module. The driver
4
+ * module replaces `__SUBSTRAT_STORE_DRIVER__` and its class name replaces
5
5
  * `__SUBSTRAT_STORE_CLASS__`.
6
6
  */
7
7
  /** The `caller` audience's public keys: raw Ed25519, base64url, comma-separated. */
8
8
  export declare const STORE_CALLER_KEYS_VAR = "SUBSTRAT_STORE_CALLER_KEYS";
9
- /** The class name the uploader replaces, inside a property access on the kind module. */
9
+ /** The class name the uploader replaces, inside a property access on the driver module. */
10
10
  export declare const STORE_CLASS_PLACEHOLDER = "__SUBSTRAT_STORE_CLASS__";
11
- /** The entry's module name in the upload. Reserved: a kind release may not ship a module by it (D-63). */
11
+ /** The import specifier the uploader replaces with `./<the driver module>`. */
12
+ export declare const STORE_DRIVER_PLACEHOLDER = "__SUBSTRAT_STORE_DRIVER__";
13
+ /** The entry's module name in the upload. Reserved: a driver release may not ship a module by it (D-63). */
12
14
  export declare const STORE_ENTRY_MODULE = "substrat-store-entry.js";
13
15
  /**
14
- * The Python entry's module name in the upload (#2290), the script's main module when the kind
16
+ * The Python entry's module name in the upload (#2290), the script's main module when the driver
15
17
  * is Python. Reserved.
16
18
  */
17
19
  export declare const STORE_ENTRY_PYTHON_MODULE = "substrat_store_entry.py";
18
- /** The import specifier the uploader replaces with `./<the kind module>`. */
19
- export declare const STORE_KIND_PLACEHOLDER = "__SUBSTRAT_STORE_KIND__";
20
20
  /** The binding the entry uses to address that class. */
21
21
  export declare const STORE_OBJECT_BINDING = "SUBSTRAT_STORE";
22
22
  /** The object class the entry addresses, and the SQLite class the upload migrates. Reserved. */
@@ -24,18 +24,18 @@ export declare const STORE_OBJECT_CLASS = "SubstratStoreObject";
24
24
  /** The `platform` audience's public keys, in the same form. */
25
25
  export declare const STORE_PLATFORM_KEYS_VAR = "SUBSTRAT_STORE_PLATFORM_KEYS";
26
26
  /**
27
- * The compatibility date a Python kind's store script is uploaded with. One constant, so the
27
+ * The compatibility date a Python driver's store script is uploaded with. One constant, so the
28
28
  * upload and the package's probe cannot run different runtimes.
29
29
  */
30
30
  export declare const STORE_PYTHON_COMPATIBILITY_DATE = "2026-03-01";
31
- /** The Python runtime's own flag. The upload adds the two that keep the entry's bindings from the kind. */
31
+ /** The Python runtime's own flag. The upload adds the two that keep the entry's bindings from the driver. */
32
32
  export declare const STORE_PYTHON_COMPATIBILITY_FLAGS: string[];
33
33
  /**
34
- * Every binding the entry owns starts with this. The kind's class is constructed with an env
35
- * that has none of them, and a kind bundle may not declare one.
34
+ * Every binding the entry owns starts with this. The driver's class is constructed with an env
35
+ * that has none of them, and a driver bundle may not declare one.
36
36
  */
37
37
  export declare const STORE_RESERVED_BINDING_PREFIX = "SUBSTRAT_STORE";
38
- /** Every module name the platform's entries own. A kind release may ship none of them (D-63). */
38
+ /** Every module name the platform's entries own. A driver release may ship none of them (D-63). */
39
39
  export declare const STORE_RESERVED_MODULES: string[];
40
40
  /** The store script's own name, as the signer writes it into the envelope's `script` line. */
41
41
  export declare const STORE_SCRIPT_VAR = "SUBSTRAT_STORE_SCRIPT";
@@ -44,17 +44,17 @@ export declare const STORE_SCRIPT_VAR = "SUBSTRAT_STORE_SCRIPT";
44
44
  * it, so both entries run one verifier. Reserved.
45
45
  */
46
46
  export declare const STORE_VERIFIER_MODULE = "substrat-store-verify.js";
47
- /** The entry itself — 11050 bytes of ESM. */
47
+ /** The entry itself — 11397 bytes of ESM. */
48
48
  export declare const STORE_ENTRY_SOURCE: string;
49
49
  /**
50
- * The Python entry (#2290), uploaded as `substrat_store_entry.py`. The kind module's
51
- * name (a Python module name, no `.py`) replaces `__SUBSTRAT_STORE_KIND__` and its class
50
+ * The Python entry (#2290), uploaded as `substrat_store_entry.py`. The driver module's
51
+ * name (a Python module name, no `.py`) replaces `__SUBSTRAT_STORE_DRIVER__` and its class
52
52
  * name replaces `__SUBSTRAT_STORE_CLASS__`, as in the JavaScript entry.
53
53
  */
54
54
  export declare const STORE_ENTRY_PYTHON_SOURCE: string;
55
55
  /**
56
56
  * The verifier the Python entry imports, uploaded beside it as `substrat-store-verify.js`
57
- * (an ES module). It is `packages/control-plane-api/store-entry/verify.ts`, the check the JavaScript entry runs — 8858 bytes.
57
+ * (an ES module). It is `packages/control-plane-api/store-entry/verify.ts`, the check the JavaScript entry runs — 9167 bytes.
58
58
  */
59
59
  export declare const STORE_ENTRY_PYTHON_VERIFIER_SOURCE: string;
60
60
  //# sourceMappingURL=store-entry.generated.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-entry.generated.d.ts","sourceRoot":"","sources":["../src/store-entry.generated.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAElE,yFAAyF;AACzF,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,0GAA0G;AAC1G,eAAO,MAAM,kBAAkB,4BAA4B,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,yBAAyB,4BAA4B,CAAC;AAEnE,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,4BAA4B,CAAC;AAEhE,wDAAwD;AACxD,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAErD,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAExD,+DAA+D;AAC/D,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,+BAA+B,eAAe,CAAC;AAE5D,2GAA2G;AAC3G,eAAO,MAAM,gCAAgC,UAAqB,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,6BAA6B,mBAAmB,CAAC;AAE9D,iGAAiG;AACjG,eAAO,MAAM,sBAAsB,UAAmF,CAAC;AAEvH,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAEhE,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,EAAE,MAA0wW,CAAC;AAE5yW;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAmoK,CAAC;AAE5qK;;;GAGG;AACH,eAAO,MAAM,kCAAkC,EAAE,MAAgjS,CAAC"}
1
+ {"version":3,"file":"store-entry.generated.d.ts","sourceRoot":"","sources":["../src/store-entry.generated.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAElE,2FAA2F;AAC3F,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,8BAA8B,CAAC;AAEpE,4GAA4G;AAC5G,eAAO,MAAM,kBAAkB,4BAA4B,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,yBAAyB,4BAA4B,CAAC;AAEnE,wDAAwD;AACxD,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAErD,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAExD,+DAA+D;AAC/D,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,+BAA+B,eAAe,CAAC;AAE5D,6GAA6G;AAC7G,eAAO,MAAM,gCAAgC,UAAqB,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,6BAA6B,mBAAmB,CAAC;AAE9D,mGAAmG;AACnG,eAAO,MAAM,sBAAsB,UAAmF,CAAC;AAEvH,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAEhE,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,EAAE,MAA8nX,CAAC;AAEhqX;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAusK,CAAC;AAEhvK;;;GAGG;AACH,eAAO,MAAM,kCAAkC,EAAE,MAA83S,CAAC"}
@@ -2,23 +2,23 @@
2
2
  // Re-emit with `pnpm lint:store-entry`; CI runs it with `--check`.
3
3
  /**
4
4
  * The store script's entry (#2289), and the names it shares with the uploader
5
- * (store-entry/names.ts). The entry is uploaded as the script's main module. The kind
6
- * module replaces `__SUBSTRAT_STORE_KIND__` and its class name replaces
5
+ * (store-entry/names.ts). The entry is uploaded as the script's main module. The driver
6
+ * module replaces `__SUBSTRAT_STORE_DRIVER__` and its class name replaces
7
7
  * `__SUBSTRAT_STORE_CLASS__`.
8
8
  */
9
9
  /** The `caller` audience's public keys: raw Ed25519, base64url, comma-separated. */
10
10
  export const STORE_CALLER_KEYS_VAR = "SUBSTRAT_STORE_CALLER_KEYS";
11
- /** The class name the uploader replaces, inside a property access on the kind module. */
11
+ /** The class name the uploader replaces, inside a property access on the driver module. */
12
12
  export const STORE_CLASS_PLACEHOLDER = "__SUBSTRAT_STORE_CLASS__";
13
- /** The entry's module name in the upload. Reserved: a kind release may not ship a module by it (D-63). */
13
+ /** The import specifier the uploader replaces with `./<the driver module>`. */
14
+ export const STORE_DRIVER_PLACEHOLDER = "__SUBSTRAT_STORE_DRIVER__";
15
+ /** The entry's module name in the upload. Reserved: a driver release may not ship a module by it (D-63). */
14
16
  export const STORE_ENTRY_MODULE = "substrat-store-entry.js";
15
17
  /**
16
- * The Python entry's module name in the upload (#2290), the script's main module when the kind
18
+ * The Python entry's module name in the upload (#2290), the script's main module when the driver
17
19
  * is Python. Reserved.
18
20
  */
19
21
  export const STORE_ENTRY_PYTHON_MODULE = "substrat_store_entry.py";
20
- /** The import specifier the uploader replaces with `./<the kind module>`. */
21
- export const STORE_KIND_PLACEHOLDER = "__SUBSTRAT_STORE_KIND__";
22
22
  /** The binding the entry uses to address that class. */
23
23
  export const STORE_OBJECT_BINDING = "SUBSTRAT_STORE";
24
24
  /** The object class the entry addresses, and the SQLite class the upload migrates. Reserved. */
@@ -26,18 +26,18 @@ export const STORE_OBJECT_CLASS = "SubstratStoreObject";
26
26
  /** The `platform` audience's public keys, in the same form. */
27
27
  export const STORE_PLATFORM_KEYS_VAR = "SUBSTRAT_STORE_PLATFORM_KEYS";
28
28
  /**
29
- * The compatibility date a Python kind's store script is uploaded with. One constant, so the
29
+ * The compatibility date a Python driver's store script is uploaded with. One constant, so the
30
30
  * upload and the package's probe cannot run different runtimes.
31
31
  */
32
32
  export const STORE_PYTHON_COMPATIBILITY_DATE = "2026-03-01";
33
- /** The Python runtime's own flag. The upload adds the two that keep the entry's bindings from the kind. */
33
+ /** The Python runtime's own flag. The upload adds the two that keep the entry's bindings from the driver. */
34
34
  export const STORE_PYTHON_COMPATIBILITY_FLAGS = ["python_workers"];
35
35
  /**
36
- * Every binding the entry owns starts with this. The kind's class is constructed with an env
37
- * that has none of them, and a kind bundle may not declare one.
36
+ * Every binding the entry owns starts with this. The driver's class is constructed with an env
37
+ * that has none of them, and a driver bundle may not declare one.
38
38
  */
39
39
  export const STORE_RESERVED_BINDING_PREFIX = "SUBSTRAT_STORE";
40
- /** Every module name the platform's entries own. A kind release may ship none of them (D-63). */
40
+ /** Every module name the platform's entries own. A driver release may ship none of them (D-63). */
41
41
  export const STORE_RESERVED_MODULES = ["substrat-store-entry.js", "substrat_store_entry.py", "substrat-store-verify.js"];
42
42
  /** The store script's own name, as the signer writes it into the envelope's `script` line. */
43
43
  export const STORE_SCRIPT_VAR = "SUBSTRAT_STORE_SCRIPT";
@@ -46,17 +46,17 @@ export const STORE_SCRIPT_VAR = "SUBSTRAT_STORE_SCRIPT";
46
46
  * it, so both entries run one verifier. Reserved.
47
47
  */
48
48
  export const STORE_VERIFIER_MODULE = "substrat-store-verify.js";
49
- /** The entry itself — 11050 bytes of ESM. */
50
- export const STORE_ENTRY_SOURCE = "// packages/control-plane-api/store-entry/entry.ts\nimport { DurableObject } from \"cloudflare:workers\";\nimport * as kindModule from \"__SUBSTRAT_STORE_KIND__\";\n\n// packages/control-plane-api/store-entry/names.ts\nvar STORE_OBJECT_BINDING = \"SUBSTRAT_STORE\";\nvar STORE_SCRIPT_VAR = \"SUBSTRAT_STORE_SCRIPT\";\nvar STORE_CALLER_KEYS_VAR = \"SUBSTRAT_STORE_CALLER_KEYS\";\nvar STORE_PLATFORM_KEYS_VAR = \"SUBSTRAT_STORE_PLATFORM_KEYS\";\nvar STORE_RESERVED_BINDING_PREFIX = \"SUBSTRAT_STORE\";\n\n// packages/contracts/dist/scope-store-envelope.js\nvar SCOPE_STORE_ENVELOPE_WINDOW_SECONDS = 60;\nvar SCOPE_STORE_ENVELOPE_HEADER = \"x-substrat-store-envelope\";\nvar SCOPE_STORE_SIGNATURE_HEADER = \"x-substrat-store-signature\";\nvar RESERVED_STORE_OPERATIONS = [\n \"substrat.export\",\n \"substrat.import\",\n \"substrat.size\",\n \"substrat.wipe\",\n \"substrat.erase\"\n];\nvar ADMITTED = {\n \"1\": {\n platform: (op) => RESERVED_STORE_OPERATIONS.includes(op),\n caller: (op) => !op.startsWith(\"substrat.\")\n }\n};\nfunction scopeStoreEnvelopeAdmits(v, aud, op) {\n const byAudience = Object.hasOwn(ADMITTED, String(v)) ? ADMITTED[String(v)] : void 0;\n if (!byAudience || !Object.hasOwn(byAudience, aud))\n return false;\n return byAudience[aud](op);\n}\nfunction bytesToBase64Url(bytes) {\n let binary = \"\";\n for (const byte of bytes)\n binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replaceAll(\"=\", \"\");\n}\nfunction base64UrlToBytes(value) {\n if (!/^[A-Za-z0-9_-]+$/.test(value))\n return void 0;\n const padded = value.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\") + \"=\".repeat((4 - value.length % 4) % 4);\n try {\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return bytes;\n } catch {\n return void 0;\n }\n}\nfunction copyBytes(bytes) {\n const copy = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(copy).set(bytes);\n return copy;\n}\nfunction isCanonicalBase64Url(text2, length) {\n const bytes = base64UrlToBytes(text2);\n return bytes?.length === length && bytesToBase64Url(bytes) === text2;\n}\nvar NONCE = /^[A-Za-z0-9_-]{22}$/;\nvar KID = /^[A-Za-z0-9_-]{11}$/;\nvar DIGEST = /^[0-9a-f]{64}$/;\nvar IAT = /^(0|[1-9]\\d{0,14})$/;\nfunction parseScopeStoreEnvelope(canonical) {\n const lines = canonical.split(\"\\n\");\n if (lines.length !== 9)\n return void 0;\n const [version, aud, kid, script, name, op, digest, iatText, nonce] = lines;\n if (version !== \"1\" || aud !== \"caller\" && aud !== \"platform\")\n return void 0;\n if (!script || !name || !op)\n return void 0;\n if (!KID.test(kid) || !DIGEST.test(digest) || !IAT.test(iatText) || !NONCE.test(nonce))\n return void 0;\n if (!isCanonicalBase64Url(kid, 8) || !isCanonicalBase64Url(nonce, 16))\n return void 0;\n return { v: 1, aud, kid, script, name, op, digest, iat: Number(iatText), nonce };\n}\nasync function scopeStoreBodyDigest(body) {\n const hash = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(body));\n return [...new Uint8Array(hash)].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction scopeStoreEnvelopeFresh(iat, nowSeconds, windowSeconds = SCOPE_STORE_ENVELOPE_WINDOW_SECONDS) {\n return Math.abs(nowSeconds - iat) <= windowSeconds;\n}\nvar SMALL_ORDER_KEYS = [\n \"0000000000000000000000000000000000000000000000000000000000000000\",\n // y = 0, order 4\n \"0100000000000000000000000000000000000000000000000000000000000000\",\n // the identity, order 1\n \"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05\",\n // order 8\n \"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a\",\n // order 8\n \"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\",\n // y = p - 1, order 2\n \"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\",\n // y = p, a non-canonical 0\n \"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\"\n // y = p + 1, a non-canonical identity\n];\nfunction isSmallOrderKey(bytes) {\n const unsigned = Uint8Array.from(bytes);\n unsigned[31] = unsigned[31] & 127;\n const hex = [...unsigned].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return SMALL_ORDER_KEYS.includes(hex);\n}\nasync function scopeStoreKeyId(publicKey) {\n const bytes = base64UrlToBytes(publicKey);\n if (!bytes || bytes.length !== 32)\n throw new Error(\"a store public key is not 32 bytes of base64url\");\n if (isSmallOrderKey(bytes))\n throw new Error(\"a store public key is a small-order point\");\n const hash = await crypto.subtle.digest(\"SHA-256\", copyBytes(bytes));\n return bytesToBase64Url(new Uint8Array(hash).slice(0, 8));\n}\nfunction parseScopeStorePublicKeys(text2) {\n const keys = text2.split(\",\").map((key) => key.trim());\n if (keys.length === 0 || keys.some((key) => key.length === 0)) {\n throw new Error(\"a store public key list is empty or has an empty entry\");\n }\n const seen = /* @__PURE__ */ new Set();\n for (const key of keys) {\n const bytes = base64UrlToBytes(key);\n if (bytes?.length !== 32)\n throw new Error(\"a store public key is not 32 bytes of base64url\");\n if (isSmallOrderKey(bytes))\n throw new Error(\"a store public key is a small-order point\");\n if (bytesToBase64Url(bytes) !== key)\n throw new Error(\"a store public key is not canonical base64url\");\n if (seen.has(key))\n throw new Error(\"a store public key is listed twice\");\n seen.add(key);\n }\n return keys;\n}\nfunction scopeStorePublicKeysFrom(config) {\n const caller = parseScopeStorePublicKeys(config.caller);\n const platform = parseScopeStorePublicKeys(config.platform);\n if (caller.some((key) => platform.includes(key))) {\n throw new Error(\"a store public key is listed for both audiences\");\n }\n return { caller, platform };\n}\nasync function verifyScopeStoreEnvelope(publicKey, canonical, signature) {\n const keyBytes = base64UrlToBytes(publicKey);\n const sigBytes = base64UrlToBytes(signature);\n if (!keyBytes || !sigBytes || sigBytes.length !== 64)\n return false;\n try {\n const key = await crypto.subtle.importKey(\"raw\", copyBytes(keyBytes), { name: \"Ed25519\" }, false, [\"verify\"]);\n return await crypto.subtle.verify(\"Ed25519\", key, copyBytes(sigBytes), new TextEncoder().encode(canonical));\n } catch {\n return false;\n }\n}\nasync function verifyScopeStoreCall(input) {\n const refuse = (refusal) => ({ ok: false, refusal });\n if (!input.envelope || !input.signature)\n return refuse(\"missing\");\n const bytes = base64UrlToBytes(input.envelope);\n if (!bytes)\n return refuse(\"malformed\");\n let canonical;\n try {\n canonical = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n } catch {\n return refuse(\"malformed\");\n }\n const envelope = parseScopeStoreEnvelope(canonical);\n if (!envelope)\n return refuse(\"malformed\");\n if (!scopeStoreEnvelopeAdmits(envelope.v, envelope.aud, envelope.op))\n return refuse(\"audience\");\n const kidsOf = async (keys) => {\n const kids = /* @__PURE__ */ new Map();\n for (const key of keys) {\n try {\n kids.set(await scopeStoreKeyId(key), key);\n } catch {\n }\n }\n return kids;\n };\n const own = await kidsOf(input.keys[envelope.aud]);\n const other = await kidsOf(input.keys[envelope.aud === \"caller\" ? \"platform\" : \"caller\"]);\n if ([...own.keys()].some((kid) => other.has(kid)))\n return refuse(\"keys\");\n const publicKey = own.get(envelope.kid);\n if (!publicKey)\n return refuse(\"kid\");\n if (!await verifyScopeStoreEnvelope(publicKey, canonical, input.signature))\n return refuse(\"signature\");\n if (envelope.digest !== await scopeStoreBodyDigest(input.body))\n return refuse(\"digest\");\n if (!scopeStoreEnvelopeFresh(envelope.iat, input.nowSeconds, input.windowSeconds))\n return refuse(\"stale\");\n if (envelope.script !== input.script)\n return refuse(\"script\");\n let parsed;\n try {\n parsed = JSON.parse(input.body);\n } catch {\n return refuse(\"body\");\n }\n const field = (key) => typeof parsed === \"object\" && parsed !== null ? Reflect.get(parsed, key) : void 0;\n if (field(\"operation\") !== envelope.op || field(\"objectName\") !== envelope.name)\n return refuse(\"body\");\n return { ok: true, envelope };\n}\n\n// packages/control-plane-api/store-entry/verify.ts\nasync function storeCallVerdict(headers, body, env) {\n const script = env[STORE_SCRIPT_VAR];\n let keys;\n try {\n if (typeof script !== \"string\" || script.length === 0) throw new Error(\"no script name\");\n keys = scopeStorePublicKeysFrom({\n caller: String(env[STORE_CALLER_KEYS_VAR] ?? \"\"),\n platform: String(env[STORE_PLATFORM_KEYS_VAR] ?? \"\")\n });\n } catch {\n return { ok: false, status: 503, message: \"this store script is not configured\" };\n }\n const verdict = await verifyScopeStoreCall({\n envelope: headers.get(SCOPE_STORE_ENVELOPE_HEADER),\n signature: headers.get(SCOPE_STORE_SIGNATURE_HEADER),\n body,\n keys,\n script,\n nowSeconds: Math.floor(Date.now() / 1e3)\n });\n return verdict.ok ? verdict : { ok: false, status: 401, message: `unauthorized: ${verdict.refusal}` };\n}\n\n// packages/control-plane-api/store-entry/entry.ts\nvar Kind = kindModule[\"__SUBSTRAT_STORE_CLASS__\"];\nvar text = (status, message) => new Response(message, { status });\nfunction kindEnvOf(env) {\n return Object.fromEntries(Object.entries(env).filter(([name]) => !name.startsWith(STORE_RESERVED_BINDING_PREFIX)));\n}\nasync function verified(request, body, env) {\n const verdict = await storeCallVerdict(request.headers, body, env);\n return verdict.ok ? verdict.envelope : text(verdict.status, verdict.message);\n}\nvar SubstratStoreObject = class extends DurableObject {\n #kind;\n /** Constructed once per object instance, on first use, so the kind keeps its in-memory state. */\n #kindObject() {\n this.#kind ??= new Kind(this.ctx, kindEnvOf(this.env));\n return this.#kind;\n }\n async fetch(request) {\n const body = await request.text();\n const envelope = await verified(request, body, this.env);\n if (envelope instanceof Response) return envelope;\n const ns = this.env[STORE_OBJECT_BINDING];\n if (!this.ctx.id.equals(ns.idFromName(envelope.name))) return text(401, \"unauthorized: object\");\n return this.#kindObject().fetch(\n new Request(request.url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body })\n );\n }\n /** An alarm is the object's own timer, which only the kind sets. It goes to the kind. */\n async alarm(alarmInfo) {\n const kind = this.#kindObject();\n if (typeof kind.alarm === \"function\") await kind.alarm(alarmInfo);\n }\n};\nvar entry_default = {\n async fetch(request, env) {\n const body = await request.text();\n const envelope = await verified(request, body, env);\n if (envelope instanceof Response) return envelope;\n const ns = env[STORE_OBJECT_BINDING];\n return ns.get(ns.idFromName(envelope.name)).fetch(\n new Request(request.url, { method: \"POST\", headers: request.headers, body })\n );\n }\n};\nexport {\n SubstratStoreObject,\n entry_default as default\n};\n";
49
+ /** The entry itself — 11397 bytes of ESM. */
50
+ export const STORE_ENTRY_SOURCE = "// packages/control-plane-api/store-entry/entry.ts\nimport { DurableObject } from \"cloudflare:workers\";\nimport * as driverModule from \"__SUBSTRAT_STORE_DRIVER__\";\n\n// packages/control-plane-api/store-entry/names.ts\nvar STORE_OBJECT_BINDING = \"SUBSTRAT_STORE\";\nvar STORE_SCRIPT_VAR = \"SUBSTRAT_STORE_SCRIPT\";\nvar STORE_CALLER_KEYS_VAR = \"SUBSTRAT_STORE_CALLER_KEYS\";\nvar STORE_PLATFORM_KEYS_VAR = \"SUBSTRAT_STORE_PLATFORM_KEYS\";\nvar STORE_RESERVED_BINDING_PREFIX = \"SUBSTRAT_STORE\";\n\n// packages/contracts/dist/scope-store-envelope.js\nvar SCOPE_STORE_ENVELOPE_WINDOW_SECONDS = 60;\nvar SCOPE_STORE_ENVELOPE_HEADER = \"x-substrat-store-envelope\";\nvar SCOPE_STORE_SIGNATURE_HEADER = \"x-substrat-store-signature\";\nvar RESERVED_STORE_OPERATIONS = [\n \"substrat.export\",\n \"substrat.import\",\n \"substrat.size\",\n \"substrat.wipe\",\n \"substrat.erase\"\n];\nvar ADMITTED = {\n \"1\": {\n platform: (op) => RESERVED_STORE_OPERATIONS.includes(op),\n caller: (op) => !op.startsWith(\"substrat.\")\n }\n};\nfunction scopeStoreEnvelopeAdmits(v, aud, op) {\n const byAudience = Object.hasOwn(ADMITTED, String(v)) ? ADMITTED[String(v)] : void 0;\n if (!byAudience || !Object.hasOwn(byAudience, aud))\n return false;\n return byAudience[aud](op);\n}\nfunction bytesToBase64Url(bytes) {\n let binary = \"\";\n for (const byte of bytes)\n binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replaceAll(\"=\", \"\");\n}\nfunction base64UrlToBytes(value) {\n if (!/^[A-Za-z0-9_-]+$/.test(value))\n return void 0;\n const padded = value.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\") + \"=\".repeat((4 - value.length % 4) % 4);\n try {\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return bytes;\n } catch {\n return void 0;\n }\n}\nfunction copyBytes(bytes) {\n const copy = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(copy).set(bytes);\n return copy;\n}\nfunction isCanonicalBase64Url(text2, length) {\n const bytes = base64UrlToBytes(text2);\n return bytes?.length === length && bytesToBase64Url(bytes) === text2;\n}\nvar NONCE = /^[A-Za-z0-9_-]{22}$/;\nvar KID = /^[A-Za-z0-9_-]{11}$/;\nvar DIGEST = /^[0-9a-f]{64}$/;\nvar IAT = /^(0|[1-9]\\d{0,14})$/;\nfunction parseScopeStoreEnvelope(canonical) {\n const lines = canonical.split(\"\\n\");\n if (lines.length !== 9)\n return void 0;\n const [version, aud, kid, script, name, op, digest, iatText, nonce] = lines;\n if (version !== \"1\" || aud !== \"caller\" && aud !== \"platform\")\n return void 0;\n if (!script || !name || !op)\n return void 0;\n if (!KID.test(kid) || !DIGEST.test(digest) || !IAT.test(iatText) || !NONCE.test(nonce))\n return void 0;\n if (!isCanonicalBase64Url(kid, 8) || !isCanonicalBase64Url(nonce, 16))\n return void 0;\n return { v: 1, aud, kid, script, name, op, digest, iat: Number(iatText), nonce };\n}\nasync function scopeStoreBodyDigest(body) {\n const hash = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(body));\n return [...new Uint8Array(hash)].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction scopeStoreEnvelopeFresh(iat, nowSeconds, windowSeconds = SCOPE_STORE_ENVELOPE_WINDOW_SECONDS) {\n return Math.abs(nowSeconds - iat) <= windowSeconds;\n}\nvar SMALL_ORDER_KEYS = [\n \"0000000000000000000000000000000000000000000000000000000000000000\",\n // y = 0, order 4\n \"0100000000000000000000000000000000000000000000000000000000000000\",\n // the identity, order 1\n \"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05\",\n // order 8\n \"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a\",\n // order 8\n \"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\",\n // y = p - 1, order 2\n \"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\",\n // y = p, a non-canonical 0\n \"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\"\n // y = p + 1, a non-canonical identity\n];\nfunction isSmallOrderKey(bytes) {\n const unsigned = Uint8Array.from(bytes);\n unsigned[31] = unsigned[31] & 127;\n const hex = [...unsigned].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return SMALL_ORDER_KEYS.includes(hex);\n}\nasync function scopeStoreKeyId(publicKey) {\n const bytes = base64UrlToBytes(publicKey);\n if (!bytes || bytes.length !== 32)\n throw new Error(\"a store public key is not 32 bytes of base64url\");\n if (isSmallOrderKey(bytes))\n throw new Error(\"a store public key is a small-order point\");\n const hash = await crypto.subtle.digest(\"SHA-256\", copyBytes(bytes));\n return bytesToBase64Url(new Uint8Array(hash).slice(0, 8));\n}\nfunction scopeStorePublicKeyProblem(key) {\n if (key.length === 0)\n return \"empty\";\n const bytes = base64UrlToBytes(key);\n if (bytes?.length !== 32)\n return \"length\";\n if (isSmallOrderKey(bytes))\n return \"small-order\";\n if (bytesToBase64Url(bytes) !== key)\n return \"non-canonical\";\n return void 0;\n}\nvar PUBLIC_KEY_PROBLEM = {\n empty: \"a store public key list is empty or has an empty entry\",\n length: \"a store public key is not 32 bytes of base64url\",\n \"small-order\": \"a store public key is a small-order point\",\n \"non-canonical\": \"a store public key is not canonical base64url\"\n};\nfunction parseScopeStorePublicKeys(text2) {\n const keys = text2.split(\",\").map((key) => key.trim());\n if (keys.some((key) => key.length === 0))\n throw new Error(PUBLIC_KEY_PROBLEM.empty);\n const seen = /* @__PURE__ */ new Set();\n for (const key of keys) {\n const problem = scopeStorePublicKeyProblem(key);\n if (problem)\n throw new Error(PUBLIC_KEY_PROBLEM[problem]);\n if (seen.has(key))\n throw new Error(\"a store public key is listed twice\");\n seen.add(key);\n }\n return keys;\n}\nfunction scopeStorePublicKeysFrom(config) {\n const caller = parseScopeStorePublicKeys(config.caller);\n const platform = parseScopeStorePublicKeys(config.platform);\n if (caller.some((key) => platform.includes(key))) {\n throw new Error(\"a store public key is listed for both audiences\");\n }\n return { caller, platform };\n}\nasync function verifyScopeStoreEnvelope(publicKey, canonical, signature) {\n const keyBytes = base64UrlToBytes(publicKey);\n const sigBytes = base64UrlToBytes(signature);\n if (!keyBytes || !sigBytes || sigBytes.length !== 64)\n return false;\n try {\n const key = await crypto.subtle.importKey(\"raw\", copyBytes(keyBytes), { name: \"Ed25519\" }, false, [\"verify\"]);\n return await crypto.subtle.verify(\"Ed25519\", key, copyBytes(sigBytes), new TextEncoder().encode(canonical));\n } catch {\n return false;\n }\n}\nasync function verifyScopeStoreCall(input) {\n const refuse = (refusal) => ({ ok: false, refusal });\n if (!input.envelope || !input.signature)\n return refuse(\"missing\");\n const bytes = base64UrlToBytes(input.envelope);\n if (!bytes)\n return refuse(\"malformed\");\n let canonical;\n try {\n canonical = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n } catch {\n return refuse(\"malformed\");\n }\n const envelope = parseScopeStoreEnvelope(canonical);\n if (!envelope)\n return refuse(\"malformed\");\n if (!scopeStoreEnvelopeAdmits(envelope.v, envelope.aud, envelope.op))\n return refuse(\"audience\");\n const kidsOf = async (keys) => {\n const kids = /* @__PURE__ */ new Map();\n for (const key of keys) {\n try {\n kids.set(await scopeStoreKeyId(key), key);\n } catch {\n }\n }\n return kids;\n };\n const own = await kidsOf(input.keys[envelope.aud]);\n const other = await kidsOf(input.keys[envelope.aud === \"caller\" ? \"platform\" : \"caller\"]);\n if ([...own.keys()].some((kid) => other.has(kid)))\n return refuse(\"keys\");\n const publicKey = own.get(envelope.kid);\n if (!publicKey)\n return refuse(\"kid\");\n if (!await verifyScopeStoreEnvelope(publicKey, canonical, input.signature))\n return refuse(\"signature\");\n if (envelope.digest !== await scopeStoreBodyDigest(input.body))\n return refuse(\"digest\");\n if (!scopeStoreEnvelopeFresh(envelope.iat, input.nowSeconds, input.windowSeconds))\n return refuse(\"stale\");\n if (envelope.script !== input.script)\n return refuse(\"script\");\n let parsed;\n try {\n parsed = JSON.parse(input.body);\n } catch {\n return refuse(\"body\");\n }\n const field = (key) => typeof parsed === \"object\" && parsed !== null ? Reflect.get(parsed, key) : void 0;\n if (field(\"operation\") !== envelope.op || field(\"objectName\") !== envelope.name)\n return refuse(\"body\");\n return { ok: true, envelope };\n}\n\n// packages/control-plane-api/store-entry/verify.ts\nasync function storeCallVerdict(headers, body, env) {\n const script = env[STORE_SCRIPT_VAR];\n let keys;\n try {\n if (typeof script !== \"string\" || script.length === 0) throw new Error(\"no script name\");\n keys = scopeStorePublicKeysFrom({\n caller: String(env[STORE_CALLER_KEYS_VAR] ?? \"\"),\n platform: String(env[STORE_PLATFORM_KEYS_VAR] ?? \"\")\n });\n } catch {\n return { ok: false, status: 503, message: \"this store script is not configured\" };\n }\n const verdict = await verifyScopeStoreCall({\n envelope: headers.get(SCOPE_STORE_ENVELOPE_HEADER),\n signature: headers.get(SCOPE_STORE_SIGNATURE_HEADER),\n body,\n keys,\n script,\n nowSeconds: Math.floor(Date.now() / 1e3)\n });\n return verdict.ok ? verdict : { ok: false, status: 401, message: `unauthorized: ${verdict.refusal}` };\n}\n\n// packages/control-plane-api/store-entry/entry.ts\nvar Driver = driverModule[\"__SUBSTRAT_STORE_CLASS__\"];\nvar text = (status, message) => new Response(message, { status });\nfunction driverEnvOf(env) {\n return Object.fromEntries(Object.entries(env).filter(([name]) => !name.startsWith(STORE_RESERVED_BINDING_PREFIX)));\n}\nasync function verified(request, body, env) {\n const verdict = await storeCallVerdict(request.headers, body, env);\n return verdict.ok ? verdict.envelope : text(verdict.status, verdict.message);\n}\nvar SubstratStoreObject = class extends DurableObject {\n #driver;\n /** Constructed once per object instance, on first use, so the driver keeps its in-memory state. */\n #driverObject() {\n this.#driver ??= new Driver(this.ctx, driverEnvOf(this.env));\n return this.#driver;\n }\n async fetch(request) {\n const body = await request.text();\n const envelope = await verified(request, body, this.env);\n if (envelope instanceof Response) return envelope;\n const ns = this.env[STORE_OBJECT_BINDING];\n if (!this.ctx.id.equals(ns.idFromName(envelope.name))) return text(401, \"unauthorized: object\");\n return this.#driverObject().fetch(\n new Request(request.url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body })\n );\n }\n /** An alarm is the object's own timer, which only the driver sets. It goes to the driver. */\n async alarm(alarmInfo) {\n const driver = this.#driverObject();\n if (typeof driver.alarm === \"function\") await driver.alarm(alarmInfo);\n }\n};\nvar entry_default = {\n async fetch(request, env) {\n const body = await request.text();\n const envelope = await verified(request, body, env);\n if (envelope instanceof Response) return envelope;\n const ns = env[STORE_OBJECT_BINDING];\n return ns.get(ns.idFromName(envelope.name)).fetch(\n new Request(request.url, { method: \"POST\", headers: request.headers, body })\n );\n }\n};\nexport {\n SubstratStoreObject,\n entry_default as default\n};\n";
51
51
  /**
52
- * The Python entry (#2290), uploaded as `substrat_store_entry.py`. The kind module's
53
- * name (a Python module name, no `.py`) replaces `__SUBSTRAT_STORE_KIND__` and its class
52
+ * The Python entry (#2290), uploaded as `substrat_store_entry.py`. The driver module's
53
+ * name (a Python module name, no `.py`) replaces `__SUBSTRAT_STORE_DRIVER__` and its class
54
54
  * name replaces `__SUBSTRAT_STORE_CLASS__`, as in the JavaScript entry.
55
55
  */
56
- export const STORE_ENTRY_PYTHON_SOURCE = "\"\"\"The platform's Python store entry (#2290), the twin of store-entry/entry.ts.\n\nUploaded as the store script's main module when the kind is Python. The kind's class is\nimported through the two placeholders below, which the upload replaces with the kind's module\nname and class name, as it does for the JavaScript entry. The kind is reached only through\nthis module, after the call is verified.\n\nThere is one verifier. Every call is checked by store-entry/verify.ts, bundled as an ES module\nthe upload ships beside this one and imported through the runtime's JavaScript bridge. It runs\ncontracts' verifyScopeStoreCall (RFC scope-stores section 10.3, checks 1-4) against the script\nname and both audiences' public keys this upload was given. This module parses no envelope, so\nit cannot disagree with the JavaScript entry about one. The default entrypoint addresses\nSubstratStoreObject by idFromName of the signed name. The object checks the call again, and\nrefuses unless its own id is the id of that name (check 5), before the kind is constructed: an\nobject is bound to one name by construction, with nothing stored that the kind could rewrite.\n\nThe body is read once, verified as that string, and that same string is what the kind receives.\nThe kind is constructed once per object instance with the object's ctx and an env with none of\nthe entry's SUBSTRAT_STORE* bindings. The upload sets disallow_importable_env and\ndisable_ctx_exports, and both apply to a Python script: `from workers import env` then reads an\nempty env, and ctx.exports is absent.\n\nWhat this is not: a boundary against a hostile kind, and it is weaker here than in JavaScript.\nPython has no private fields. The kind's module is imported by this one, before this module's\nbody has finished, and runs in the same interpreter: it can reach this module through\nsys.modules and replace its functions, and it can reach the entry's object, its full env and the\nstore namespace in it, through the garbage collector. The env strip and the cached kind keep a\ncareless kind away from the entry's bindings, not a hostile one. A kind written to interfere is\nstopped by the review of its release (D-63), not by this module.\n\"\"\"\n\nfrom workers import DurableObject, Request, Response, WorkerEntrypoint, import_from_javascript\nfrom js import Object\n\nfrom __SUBSTRAT_STORE_KIND__ import __SUBSTRAT_STORE_CLASS__ as StoreKind\n\n# Each of these is a name in store-entry/names.ts; test/python-entry.test.ts holds them equal.\nVERIFIER_MODULE = \"./substrat-store-verify.js\"\nOBJECT_BINDING = \"SUBSTRAT_STORE\"\nRESERVED_BINDING_PREFIX = \"SUBSTRAT_STORE\"\n\n\ndef _raw_env(env):\n \"\"\"The JavaScript env the runtime handed over. The SDK wraps it, and has no public unwrap.\"\"\"\n return env._env\n\n\ndef _kind_env(env):\n \"\"\"The env the kind is constructed with: everything but the entry's own bindings.\"\"\"\n entries = [entry for entry in Object.entries(_raw_env(env)) if not str(entry[0]).startswith(RESERVED_BINDING_PREFIX)]\n return Object.fromEntries(entries)\n\n\nasync def _verified(request, body, env):\n \"\"\"The verified envelope's object name, or the response that refuses the call.\"\"\"\n verifier = import_from_javascript(VERIFIER_MODULE)\n verdict = await verifier.storeCallVerdict(request.js_object.headers, body, _raw_env(env))\n if not verdict.ok:\n return Response(verdict.message, status=verdict.status)\n return verdict.envelope.name\n\n\ndef _kind_request(url, body):\n return Request(url, method=\"POST\", headers={\"content-type\": \"application/json\"}, body=body)\n\n\nclass SubstratStoreObject(DurableObject):\n def __init__(self, ctx, env):\n super().__init__(ctx, env)\n self._kind = None\n\n def _kind_object(self):\n \"\"\"Constructed once per object instance, on first use, so the kind keeps its in-memory state.\"\"\"\n if self._kind is None:\n self._kind = StoreKind(self.ctx, _kind_env(self.env))\n return self._kind\n\n async def fetch(self, request):\n body = await request.text()\n name = await _verified(request, body, self.env)\n if isinstance(name, Response):\n return name\n namespace = getattr(self.env, OBJECT_BINDING)\n if not self.ctx.id.equals(namespace.idFromName(name)):\n return Response(\"unauthorized: object\", status=401)\n return await self._kind_object().fetch(_kind_request(request.url, body))\n\n async def alarm(self, alarm_info=None):\n \"\"\"An alarm is the object's own timer, which only the kind sets. It goes to the kind.\"\"\"\n kind = self._kind_object()\n if hasattr(kind, \"alarm\"):\n await kind.alarm(alarm_info)\n\n\nclass Default(WorkerEntrypoint):\n async def fetch(self, request):\n body = await request.text()\n name = await _verified(request, body, self.env)\n if isinstance(name, Response):\n return name\n namespace = getattr(self.env, OBJECT_BINDING)\n stub = namespace.get(namespace.idFromName(name))\n return await stub.fetch(request.url, method=\"POST\", headers=request.js_object.headers, body=body)\n";
56
+ export const STORE_ENTRY_PYTHON_SOURCE = "\"\"\"The platform's Python store entry (#2290), the twin of store-entry/entry.ts.\n\nUploaded as the store script's main module when the driver is Python. The driver's class is\nimported through the two placeholders below, which the upload replaces with the driver's module\nname and class name, as it does for the JavaScript entry. The driver is reached only through\nthis module, after the call is verified.\n\nThere is one verifier. Every call is checked by store-entry/verify.ts, bundled as an ES module\nthe upload ships beside this one and imported through the runtime's JavaScript bridge. It runs\ncontracts' verifyScopeStoreCall (RFC scope-stores section 10.3, checks 1-4) against the script\nname and both audiences' public keys this upload was given. This module parses no envelope, so\nit cannot disagree with the JavaScript entry about one. The default entrypoint addresses\nSubstratStoreObject by idFromName of the signed name. The object checks the call again, and\nrefuses unless its own id is the id of that name (check 5), before the driver is constructed: an\nobject is bound to one name by construction, with nothing stored that the driver could rewrite.\n\nThe body is read once, verified as that string, and that same string is what the driver receives.\nThe driver is constructed once per object instance with the object's ctx and an env with none of\nthe entry's SUBSTRAT_STORE* bindings. The upload sets disallow_importable_env and\ndisable_ctx_exports, and both apply to a Python script: `from workers import env` then reads an\nempty env, and ctx.exports is absent.\n\nWhat this is not: a boundary against a hostile driver, and it is weaker here than in JavaScript.\nPython has no private fields. The driver's module is imported by this one, before this module's\nbody has finished, and runs in the same interpreter: it can reach this module through\nsys.modules and replace its functions, and it can reach the entry's object, its full env and the\nstore namespace in it, through the garbage collector. The env strip and the cached driver keep a\ncareless driver away from the entry's bindings, not a hostile one. A driver written to interfere is\nstopped by the review of its release (D-63), not by this module.\n\"\"\"\n\nfrom workers import DurableObject, Request, Response, WorkerEntrypoint, import_from_javascript\nfrom js import Object\n\nfrom __SUBSTRAT_STORE_DRIVER__ import __SUBSTRAT_STORE_CLASS__ as StoreDriver\n\n# Each of these is a name in store-entry/names.ts; test/python-entry.test.ts holds them equal.\nVERIFIER_MODULE = \"./substrat-store-verify.js\"\nOBJECT_BINDING = \"SUBSTRAT_STORE\"\nRESERVED_BINDING_PREFIX = \"SUBSTRAT_STORE\"\n\n\ndef _raw_env(env):\n \"\"\"The JavaScript env the runtime handed over. The SDK wraps it, and has no public unwrap.\"\"\"\n return env._env\n\n\ndef _driver_env(env):\n \"\"\"The env the driver is constructed with: everything but the entry's own bindings.\"\"\"\n entries = [entry for entry in Object.entries(_raw_env(env)) if not str(entry[0]).startswith(RESERVED_BINDING_PREFIX)]\n return Object.fromEntries(entries)\n\n\nasync def _verified(request, body, env):\n \"\"\"The verified envelope's object name, or the response that refuses the call.\"\"\"\n verifier = import_from_javascript(VERIFIER_MODULE)\n verdict = await verifier.storeCallVerdict(request.js_object.headers, body, _raw_env(env))\n if not verdict.ok:\n return Response(verdict.message, status=verdict.status)\n return verdict.envelope.name\n\n\ndef _driver_request(url, body):\n return Request(url, method=\"POST\", headers={\"content-type\": \"application/json\"}, body=body)\n\n\nclass SubstratStoreObject(DurableObject):\n def __init__(self, ctx, env):\n super().__init__(ctx, env)\n self._driver = None\n\n def _driver_object(self):\n \"\"\"Constructed once per object instance, on first use, so the driver keeps its in-memory state.\"\"\"\n if self._driver is None:\n self._driver = StoreDriver(self.ctx, _driver_env(self.env))\n return self._driver\n\n async def fetch(self, request):\n body = await request.text()\n name = await _verified(request, body, self.env)\n if isinstance(name, Response):\n return name\n namespace = getattr(self.env, OBJECT_BINDING)\n if not self.ctx.id.equals(namespace.idFromName(name)):\n return Response(\"unauthorized: object\", status=401)\n return await self._driver_object().fetch(_driver_request(request.url, body))\n\n async def alarm(self, alarm_info=None):\n \"\"\"An alarm is the object's own timer, which only the driver sets. It goes to the driver.\"\"\"\n driver = self._driver_object()\n if hasattr(driver, \"alarm\"):\n await driver.alarm(alarm_info)\n\n\nclass Default(WorkerEntrypoint):\n async def fetch(self, request):\n body = await request.text()\n name = await _verified(request, body, self.env)\n if isinstance(name, Response):\n return name\n namespace = getattr(self.env, OBJECT_BINDING)\n stub = namespace.get(namespace.idFromName(name))\n return await stub.fetch(request.url, method=\"POST\", headers=request.js_object.headers, body=body)\n";
57
57
  /**
58
58
  * The verifier the Python entry imports, uploaded beside it as `substrat-store-verify.js`
59
- * (an ES module). It is `packages/control-plane-api/store-entry/verify.ts`, the check the JavaScript entry runs — 8858 bytes.
59
+ * (an ES module). It is `packages/control-plane-api/store-entry/verify.ts`, the check the JavaScript entry runs — 9167 bytes.
60
60
  */
61
- export const STORE_ENTRY_PYTHON_VERIFIER_SOURCE = "// packages/contracts/dist/scope-store-envelope.js\nvar SCOPE_STORE_ENVELOPE_WINDOW_SECONDS = 60;\nvar SCOPE_STORE_ENVELOPE_HEADER = \"x-substrat-store-envelope\";\nvar SCOPE_STORE_SIGNATURE_HEADER = \"x-substrat-store-signature\";\nvar RESERVED_STORE_OPERATIONS = [\n \"substrat.export\",\n \"substrat.import\",\n \"substrat.size\",\n \"substrat.wipe\",\n \"substrat.erase\"\n];\nvar ADMITTED = {\n \"1\": {\n platform: (op) => RESERVED_STORE_OPERATIONS.includes(op),\n caller: (op) => !op.startsWith(\"substrat.\")\n }\n};\nfunction scopeStoreEnvelopeAdmits(v, aud, op) {\n const byAudience = Object.hasOwn(ADMITTED, String(v)) ? ADMITTED[String(v)] : void 0;\n if (!byAudience || !Object.hasOwn(byAudience, aud))\n return false;\n return byAudience[aud](op);\n}\nfunction bytesToBase64Url(bytes) {\n let binary = \"\";\n for (const byte of bytes)\n binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replaceAll(\"=\", \"\");\n}\nfunction base64UrlToBytes(value) {\n if (!/^[A-Za-z0-9_-]+$/.test(value))\n return void 0;\n const padded = value.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\") + \"=\".repeat((4 - value.length % 4) % 4);\n try {\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return bytes;\n } catch {\n return void 0;\n }\n}\nfunction copyBytes(bytes) {\n const copy = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(copy).set(bytes);\n return copy;\n}\nfunction isCanonicalBase64Url(text, length) {\n const bytes = base64UrlToBytes(text);\n return bytes?.length === length && bytesToBase64Url(bytes) === text;\n}\nvar NONCE = /^[A-Za-z0-9_-]{22}$/;\nvar KID = /^[A-Za-z0-9_-]{11}$/;\nvar DIGEST = /^[0-9a-f]{64}$/;\nvar IAT = /^(0|[1-9]\\d{0,14})$/;\nfunction parseScopeStoreEnvelope(canonical) {\n const lines = canonical.split(\"\\n\");\n if (lines.length !== 9)\n return void 0;\n const [version, aud, kid, script, name, op, digest, iatText, nonce] = lines;\n if (version !== \"1\" || aud !== \"caller\" && aud !== \"platform\")\n return void 0;\n if (!script || !name || !op)\n return void 0;\n if (!KID.test(kid) || !DIGEST.test(digest) || !IAT.test(iatText) || !NONCE.test(nonce))\n return void 0;\n if (!isCanonicalBase64Url(kid, 8) || !isCanonicalBase64Url(nonce, 16))\n return void 0;\n return { v: 1, aud, kid, script, name, op, digest, iat: Number(iatText), nonce };\n}\nasync function scopeStoreBodyDigest(body) {\n const hash = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(body));\n return [...new Uint8Array(hash)].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction scopeStoreEnvelopeFresh(iat, nowSeconds, windowSeconds = SCOPE_STORE_ENVELOPE_WINDOW_SECONDS) {\n return Math.abs(nowSeconds - iat) <= windowSeconds;\n}\nvar SMALL_ORDER_KEYS = [\n \"0000000000000000000000000000000000000000000000000000000000000000\",\n // y = 0, order 4\n \"0100000000000000000000000000000000000000000000000000000000000000\",\n // the identity, order 1\n \"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05\",\n // order 8\n \"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a\",\n // order 8\n \"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\",\n // y = p - 1, order 2\n \"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\",\n // y = p, a non-canonical 0\n \"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\"\n // y = p + 1, a non-canonical identity\n];\nfunction isSmallOrderKey(bytes) {\n const unsigned = Uint8Array.from(bytes);\n unsigned[31] = unsigned[31] & 127;\n const hex = [...unsigned].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return SMALL_ORDER_KEYS.includes(hex);\n}\nasync function scopeStoreKeyId(publicKey) {\n const bytes = base64UrlToBytes(publicKey);\n if (!bytes || bytes.length !== 32)\n throw new Error(\"a store public key is not 32 bytes of base64url\");\n if (isSmallOrderKey(bytes))\n throw new Error(\"a store public key is a small-order point\");\n const hash = await crypto.subtle.digest(\"SHA-256\", copyBytes(bytes));\n return bytesToBase64Url(new Uint8Array(hash).slice(0, 8));\n}\nfunction parseScopeStorePublicKeys(text) {\n const keys = text.split(\",\").map((key) => key.trim());\n if (keys.length === 0 || keys.some((key) => key.length === 0)) {\n throw new Error(\"a store public key list is empty or has an empty entry\");\n }\n const seen = /* @__PURE__ */ new Set();\n for (const key of keys) {\n const bytes = base64UrlToBytes(key);\n if (bytes?.length !== 32)\n throw new Error(\"a store public key is not 32 bytes of base64url\");\n if (isSmallOrderKey(bytes))\n throw new Error(\"a store public key is a small-order point\");\n if (bytesToBase64Url(bytes) !== key)\n throw new Error(\"a store public key is not canonical base64url\");\n if (seen.has(key))\n throw new Error(\"a store public key is listed twice\");\n seen.add(key);\n }\n return keys;\n}\nfunction scopeStorePublicKeysFrom(config) {\n const caller = parseScopeStorePublicKeys(config.caller);\n const platform = parseScopeStorePublicKeys(config.platform);\n if (caller.some((key) => platform.includes(key))) {\n throw new Error(\"a store public key is listed for both audiences\");\n }\n return { caller, platform };\n}\nasync function verifyScopeStoreEnvelope(publicKey, canonical, signature) {\n const keyBytes = base64UrlToBytes(publicKey);\n const sigBytes = base64UrlToBytes(signature);\n if (!keyBytes || !sigBytes || sigBytes.length !== 64)\n return false;\n try {\n const key = await crypto.subtle.importKey(\"raw\", copyBytes(keyBytes), { name: \"Ed25519\" }, false, [\"verify\"]);\n return await crypto.subtle.verify(\"Ed25519\", key, copyBytes(sigBytes), new TextEncoder().encode(canonical));\n } catch {\n return false;\n }\n}\nasync function verifyScopeStoreCall(input) {\n const refuse = (refusal) => ({ ok: false, refusal });\n if (!input.envelope || !input.signature)\n return refuse(\"missing\");\n const bytes = base64UrlToBytes(input.envelope);\n if (!bytes)\n return refuse(\"malformed\");\n let canonical;\n try {\n canonical = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n } catch {\n return refuse(\"malformed\");\n }\n const envelope = parseScopeStoreEnvelope(canonical);\n if (!envelope)\n return refuse(\"malformed\");\n if (!scopeStoreEnvelopeAdmits(envelope.v, envelope.aud, envelope.op))\n return refuse(\"audience\");\n const kidsOf = async (keys) => {\n const kids = /* @__PURE__ */ new Map();\n for (const key of keys) {\n try {\n kids.set(await scopeStoreKeyId(key), key);\n } catch {\n }\n }\n return kids;\n };\n const own = await kidsOf(input.keys[envelope.aud]);\n const other = await kidsOf(input.keys[envelope.aud === \"caller\" ? \"platform\" : \"caller\"]);\n if ([...own.keys()].some((kid) => other.has(kid)))\n return refuse(\"keys\");\n const publicKey = own.get(envelope.kid);\n if (!publicKey)\n return refuse(\"kid\");\n if (!await verifyScopeStoreEnvelope(publicKey, canonical, input.signature))\n return refuse(\"signature\");\n if (envelope.digest !== await scopeStoreBodyDigest(input.body))\n return refuse(\"digest\");\n if (!scopeStoreEnvelopeFresh(envelope.iat, input.nowSeconds, input.windowSeconds))\n return refuse(\"stale\");\n if (envelope.script !== input.script)\n return refuse(\"script\");\n let parsed;\n try {\n parsed = JSON.parse(input.body);\n } catch {\n return refuse(\"body\");\n }\n const field = (key) => typeof parsed === \"object\" && parsed !== null ? Reflect.get(parsed, key) : void 0;\n if (field(\"operation\") !== envelope.op || field(\"objectName\") !== envelope.name)\n return refuse(\"body\");\n return { ok: true, envelope };\n}\n\n// packages/control-plane-api/store-entry/names.ts\nvar STORE_SCRIPT_VAR = \"SUBSTRAT_STORE_SCRIPT\";\nvar STORE_CALLER_KEYS_VAR = \"SUBSTRAT_STORE_CALLER_KEYS\";\nvar STORE_PLATFORM_KEYS_VAR = \"SUBSTRAT_STORE_PLATFORM_KEYS\";\n\n// packages/control-plane-api/store-entry/verify.ts\nasync function storeCallVerdict(headers, body, env) {\n const script = env[STORE_SCRIPT_VAR];\n let keys;\n try {\n if (typeof script !== \"string\" || script.length === 0) throw new Error(\"no script name\");\n keys = scopeStorePublicKeysFrom({\n caller: String(env[STORE_CALLER_KEYS_VAR] ?? \"\"),\n platform: String(env[STORE_PLATFORM_KEYS_VAR] ?? \"\")\n });\n } catch {\n return { ok: false, status: 503, message: \"this store script is not configured\" };\n }\n const verdict = await verifyScopeStoreCall({\n envelope: headers.get(SCOPE_STORE_ENVELOPE_HEADER),\n signature: headers.get(SCOPE_STORE_SIGNATURE_HEADER),\n body,\n keys,\n script,\n nowSeconds: Math.floor(Date.now() / 1e3)\n });\n return verdict.ok ? verdict : { ok: false, status: 401, message: `unauthorized: ${verdict.refusal}` };\n}\nexport {\n storeCallVerdict\n};\n";
61
+ export const STORE_ENTRY_PYTHON_VERIFIER_SOURCE = "// packages/contracts/dist/scope-store-envelope.js\nvar SCOPE_STORE_ENVELOPE_WINDOW_SECONDS = 60;\nvar SCOPE_STORE_ENVELOPE_HEADER = \"x-substrat-store-envelope\";\nvar SCOPE_STORE_SIGNATURE_HEADER = \"x-substrat-store-signature\";\nvar RESERVED_STORE_OPERATIONS = [\n \"substrat.export\",\n \"substrat.import\",\n \"substrat.size\",\n \"substrat.wipe\",\n \"substrat.erase\"\n];\nvar ADMITTED = {\n \"1\": {\n platform: (op) => RESERVED_STORE_OPERATIONS.includes(op),\n caller: (op) => !op.startsWith(\"substrat.\")\n }\n};\nfunction scopeStoreEnvelopeAdmits(v, aud, op) {\n const byAudience = Object.hasOwn(ADMITTED, String(v)) ? ADMITTED[String(v)] : void 0;\n if (!byAudience || !Object.hasOwn(byAudience, aud))\n return false;\n return byAudience[aud](op);\n}\nfunction bytesToBase64Url(bytes) {\n let binary = \"\";\n for (const byte of bytes)\n binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replaceAll(\"=\", \"\");\n}\nfunction base64UrlToBytes(value) {\n if (!/^[A-Za-z0-9_-]+$/.test(value))\n return void 0;\n const padded = value.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\") + \"=\".repeat((4 - value.length % 4) % 4);\n try {\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return bytes;\n } catch {\n return void 0;\n }\n}\nfunction copyBytes(bytes) {\n const copy = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(copy).set(bytes);\n return copy;\n}\nfunction isCanonicalBase64Url(text, length) {\n const bytes = base64UrlToBytes(text);\n return bytes?.length === length && bytesToBase64Url(bytes) === text;\n}\nvar NONCE = /^[A-Za-z0-9_-]{22}$/;\nvar KID = /^[A-Za-z0-9_-]{11}$/;\nvar DIGEST = /^[0-9a-f]{64}$/;\nvar IAT = /^(0|[1-9]\\d{0,14})$/;\nfunction parseScopeStoreEnvelope(canonical) {\n const lines = canonical.split(\"\\n\");\n if (lines.length !== 9)\n return void 0;\n const [version, aud, kid, script, name, op, digest, iatText, nonce] = lines;\n if (version !== \"1\" || aud !== \"caller\" && aud !== \"platform\")\n return void 0;\n if (!script || !name || !op)\n return void 0;\n if (!KID.test(kid) || !DIGEST.test(digest) || !IAT.test(iatText) || !NONCE.test(nonce))\n return void 0;\n if (!isCanonicalBase64Url(kid, 8) || !isCanonicalBase64Url(nonce, 16))\n return void 0;\n return { v: 1, aud, kid, script, name, op, digest, iat: Number(iatText), nonce };\n}\nasync function scopeStoreBodyDigest(body) {\n const hash = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(body));\n return [...new Uint8Array(hash)].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction scopeStoreEnvelopeFresh(iat, nowSeconds, windowSeconds = SCOPE_STORE_ENVELOPE_WINDOW_SECONDS) {\n return Math.abs(nowSeconds - iat) <= windowSeconds;\n}\nvar SMALL_ORDER_KEYS = [\n \"0000000000000000000000000000000000000000000000000000000000000000\",\n // y = 0, order 4\n \"0100000000000000000000000000000000000000000000000000000000000000\",\n // the identity, order 1\n \"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05\",\n // order 8\n \"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a\",\n // order 8\n \"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\",\n // y = p - 1, order 2\n \"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\",\n // y = p, a non-canonical 0\n \"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f\"\n // y = p + 1, a non-canonical identity\n];\nfunction isSmallOrderKey(bytes) {\n const unsigned = Uint8Array.from(bytes);\n unsigned[31] = unsigned[31] & 127;\n const hex = [...unsigned].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return SMALL_ORDER_KEYS.includes(hex);\n}\nasync function scopeStoreKeyId(publicKey) {\n const bytes = base64UrlToBytes(publicKey);\n if (!bytes || bytes.length !== 32)\n throw new Error(\"a store public key is not 32 bytes of base64url\");\n if (isSmallOrderKey(bytes))\n throw new Error(\"a store public key is a small-order point\");\n const hash = await crypto.subtle.digest(\"SHA-256\", copyBytes(bytes));\n return bytesToBase64Url(new Uint8Array(hash).slice(0, 8));\n}\nfunction scopeStorePublicKeyProblem(key) {\n if (key.length === 0)\n return \"empty\";\n const bytes = base64UrlToBytes(key);\n if (bytes?.length !== 32)\n return \"length\";\n if (isSmallOrderKey(bytes))\n return \"small-order\";\n if (bytesToBase64Url(bytes) !== key)\n return \"non-canonical\";\n return void 0;\n}\nvar PUBLIC_KEY_PROBLEM = {\n empty: \"a store public key list is empty or has an empty entry\",\n length: \"a store public key is not 32 bytes of base64url\",\n \"small-order\": \"a store public key is a small-order point\",\n \"non-canonical\": \"a store public key is not canonical base64url\"\n};\nfunction parseScopeStorePublicKeys(text) {\n const keys = text.split(\",\").map((key) => key.trim());\n if (keys.some((key) => key.length === 0))\n throw new Error(PUBLIC_KEY_PROBLEM.empty);\n const seen = /* @__PURE__ */ new Set();\n for (const key of keys) {\n const problem = scopeStorePublicKeyProblem(key);\n if (problem)\n throw new Error(PUBLIC_KEY_PROBLEM[problem]);\n if (seen.has(key))\n throw new Error(\"a store public key is listed twice\");\n seen.add(key);\n }\n return keys;\n}\nfunction scopeStorePublicKeysFrom(config) {\n const caller = parseScopeStorePublicKeys(config.caller);\n const platform = parseScopeStorePublicKeys(config.platform);\n if (caller.some((key) => platform.includes(key))) {\n throw new Error(\"a store public key is listed for both audiences\");\n }\n return { caller, platform };\n}\nasync function verifyScopeStoreEnvelope(publicKey, canonical, signature) {\n const keyBytes = base64UrlToBytes(publicKey);\n const sigBytes = base64UrlToBytes(signature);\n if (!keyBytes || !sigBytes || sigBytes.length !== 64)\n return false;\n try {\n const key = await crypto.subtle.importKey(\"raw\", copyBytes(keyBytes), { name: \"Ed25519\" }, false, [\"verify\"]);\n return await crypto.subtle.verify(\"Ed25519\", key, copyBytes(sigBytes), new TextEncoder().encode(canonical));\n } catch {\n return false;\n }\n}\nasync function verifyScopeStoreCall(input) {\n const refuse = (refusal) => ({ ok: false, refusal });\n if (!input.envelope || !input.signature)\n return refuse(\"missing\");\n const bytes = base64UrlToBytes(input.envelope);\n if (!bytes)\n return refuse(\"malformed\");\n let canonical;\n try {\n canonical = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n } catch {\n return refuse(\"malformed\");\n }\n const envelope = parseScopeStoreEnvelope(canonical);\n if (!envelope)\n return refuse(\"malformed\");\n if (!scopeStoreEnvelopeAdmits(envelope.v, envelope.aud, envelope.op))\n return refuse(\"audience\");\n const kidsOf = async (keys) => {\n const kids = /* @__PURE__ */ new Map();\n for (const key of keys) {\n try {\n kids.set(await scopeStoreKeyId(key), key);\n } catch {\n }\n }\n return kids;\n };\n const own = await kidsOf(input.keys[envelope.aud]);\n const other = await kidsOf(input.keys[envelope.aud === \"caller\" ? \"platform\" : \"caller\"]);\n if ([...own.keys()].some((kid) => other.has(kid)))\n return refuse(\"keys\");\n const publicKey = own.get(envelope.kid);\n if (!publicKey)\n return refuse(\"kid\");\n if (!await verifyScopeStoreEnvelope(publicKey, canonical, input.signature))\n return refuse(\"signature\");\n if (envelope.digest !== await scopeStoreBodyDigest(input.body))\n return refuse(\"digest\");\n if (!scopeStoreEnvelopeFresh(envelope.iat, input.nowSeconds, input.windowSeconds))\n return refuse(\"stale\");\n if (envelope.script !== input.script)\n return refuse(\"script\");\n let parsed;\n try {\n parsed = JSON.parse(input.body);\n } catch {\n return refuse(\"body\");\n }\n const field = (key) => typeof parsed === \"object\" && parsed !== null ? Reflect.get(parsed, key) : void 0;\n if (field(\"operation\") !== envelope.op || field(\"objectName\") !== envelope.name)\n return refuse(\"body\");\n return { ok: true, envelope };\n}\n\n// packages/control-plane-api/store-entry/names.ts\nvar STORE_SCRIPT_VAR = \"SUBSTRAT_STORE_SCRIPT\";\nvar STORE_CALLER_KEYS_VAR = \"SUBSTRAT_STORE_CALLER_KEYS\";\nvar STORE_PLATFORM_KEYS_VAR = \"SUBSTRAT_STORE_PLATFORM_KEYS\";\n\n// packages/control-plane-api/store-entry/verify.ts\nasync function storeCallVerdict(headers, body, env) {\n const script = env[STORE_SCRIPT_VAR];\n let keys;\n try {\n if (typeof script !== \"string\" || script.length === 0) throw new Error(\"no script name\");\n keys = scopeStorePublicKeysFrom({\n caller: String(env[STORE_CALLER_KEYS_VAR] ?? \"\"),\n platform: String(env[STORE_PLATFORM_KEYS_VAR] ?? \"\")\n });\n } catch {\n return { ok: false, status: 503, message: \"this store script is not configured\" };\n }\n const verdict = await verifyScopeStoreCall({\n envelope: headers.get(SCOPE_STORE_ENVELOPE_HEADER),\n signature: headers.get(SCOPE_STORE_SIGNATURE_HEADER),\n body,\n keys,\n script,\n nowSeconds: Math.floor(Date.now() / 1e3)\n });\n return verdict.ok ? verdict : { ok: false, status: 401, message: `unauthorized: ${verdict.refusal}` };\n}\nexport {\n storeCallVerdict\n};\n";
62
62
  //# sourceMappingURL=store-entry.generated.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-entry.generated.js","sourceRoot":"","sources":["../src/store-entry.generated.ts"],"names":[],"mappings":"AAAA,oOAAoO;AACpO,mEAAmE;AAEnE;;;;;GAKG;AAEH,oFAAoF;AACpF,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAElE,yFAAyF;AACzF,MAAM,CAAC,MAAM,uBAAuB,GAAG,0BAA0B,CAAC;AAElE,0GAA0G;AAC1G,MAAM,CAAC,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;AAE5D;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,yBAAyB,CAAC;AAEnE,6EAA6E;AAC7E,MAAM,CAAC,MAAM,sBAAsB,GAAG,yBAAyB,CAAC;AAEhE,wDAAwD;AACxD,MAAM,CAAC,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAErD,gGAAgG;AAChG,MAAM,CAAC,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAExD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,uBAAuB,GAAG,8BAA8B,CAAC;AAEtE;;;GAGG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,YAAY,CAAC;AAE5D,2GAA2G;AAC3G,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAEnE;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,gBAAgB,CAAC;AAE9D,iGAAiG;AACjG,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,yBAAyB,EAAC,yBAAyB,EAAC,0BAA0B,CAAC,CAAC;AAEvH,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAExD;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AAEhE,6CAA6C;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAW,iwWAAiwW,CAAC;AAE5yW;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAW,0nKAA0nK,CAAC;AAE5qK;;;GAGG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAW,uiSAAuiS,CAAC"}
1
+ {"version":3,"file":"store-entry.generated.js","sourceRoot":"","sources":["../src/store-entry.generated.ts"],"names":[],"mappings":"AAAA,oOAAoO;AACpO,mEAAmE;AAEnE;;;;;GAKG;AAEH,oFAAoF;AACpF,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAElE,2FAA2F;AAC3F,MAAM,CAAC,MAAM,uBAAuB,GAAG,0BAA0B,CAAC;AAElE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,2BAA2B,CAAC;AAEpE,4GAA4G;AAC5G,MAAM,CAAC,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;AAE5D;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,yBAAyB,CAAC;AAEnE,wDAAwD;AACxD,MAAM,CAAC,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAErD,gGAAgG;AAChG,MAAM,CAAC,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAExD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,uBAAuB,GAAG,8BAA8B,CAAC;AAEtE;;;GAGG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,YAAY,CAAC;AAE5D,6GAA6G;AAC7G,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAEnE;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,gBAAgB,CAAC;AAE9D,mGAAmG;AACnG,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,yBAAyB,EAAC,yBAAyB,EAAC,0BAA0B,CAAC,CAAC;AAEvH,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAExD;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AAEhE,6CAA6C;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAW,qnXAAqnX,CAAC;AAEhqX;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAW,8rKAA8rK,CAAC;AAEhvK;;;GAGG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAW,q3SAAq3S,CAAC"}
@@ -1,33 +1,33 @@
1
1
  import { scopeStorePublicKeysFrom } from '@substrat-run/contracts';
2
- import { STORE_CALLER_KEYS_VAR, STORE_CLASS_PLACEHOLDER, STORE_ENTRY_MODULE, STORE_ENTRY_PYTHON_MODULE, STORE_ENTRY_PYTHON_SOURCE, STORE_ENTRY_PYTHON_VERIFIER_SOURCE, STORE_ENTRY_SOURCE, STORE_KIND_PLACEHOLDER, STORE_OBJECT_BINDING, STORE_OBJECT_CLASS, STORE_PLATFORM_KEYS_VAR, STORE_PYTHON_COMPATIBILITY_DATE, STORE_PYTHON_COMPATIBILITY_FLAGS, STORE_RESERVED_BINDING_PREFIX, STORE_RESERVED_MODULES, STORE_SCRIPT_VAR, STORE_VERIFIER_MODULE, } from './store-entry.generated.js';
3
- export { STORE_CLASS_PLACEHOLDER, STORE_ENTRY_MODULE, STORE_ENTRY_PYTHON_MODULE, STORE_ENTRY_PYTHON_SOURCE, STORE_ENTRY_PYTHON_VERIFIER_SOURCE, STORE_ENTRY_SOURCE, STORE_KIND_PLACEHOLDER, STORE_OBJECT_BINDING, STORE_OBJECT_CLASS, STORE_PYTHON_COMPATIBILITY_DATE, STORE_RESERVED_MODULES, STORE_VERIFIER_MODULE, } from './store-entry.generated.js';
2
+ import { STORE_CALLER_KEYS_VAR, STORE_CLASS_PLACEHOLDER, STORE_ENTRY_MODULE, STORE_ENTRY_PYTHON_MODULE, STORE_ENTRY_PYTHON_SOURCE, STORE_ENTRY_PYTHON_VERIFIER_SOURCE, STORE_ENTRY_SOURCE, STORE_DRIVER_PLACEHOLDER, STORE_OBJECT_BINDING, STORE_OBJECT_CLASS, STORE_PLATFORM_KEYS_VAR, STORE_PYTHON_COMPATIBILITY_DATE, STORE_PYTHON_COMPATIBILITY_FLAGS, STORE_RESERVED_BINDING_PREFIX, STORE_RESERVED_MODULES, STORE_SCRIPT_VAR, STORE_VERIFIER_MODULE, } from './store-entry.generated.js';
3
+ export { STORE_CLASS_PLACEHOLDER, STORE_ENTRY_MODULE, STORE_ENTRY_PYTHON_MODULE, STORE_ENTRY_PYTHON_SOURCE, STORE_ENTRY_PYTHON_VERIFIER_SOURCE, STORE_ENTRY_SOURCE, STORE_DRIVER_PLACEHOLDER, STORE_OBJECT_BINDING, STORE_OBJECT_CLASS, STORE_PYTHON_COMPATIBILITY_DATE, STORE_RESERVED_MODULES, STORE_VERIFIER_MODULE, } from './store-entry.generated.js';
4
4
  /** A module name the uploader can write and the entry can import: relative, no `..`, JavaScript. */
5
5
  const MODULE_NAME = /^(?!.*\.\.)[A-Za-z0-9_][A-Za-z0-9_./-]*\.m?js$/;
6
6
  /**
7
7
  * The compatibility flag that empties `import { env } from 'cloudflare:workers'`. Without it a
8
- * kind module reads every binding of the script that way, the entry's own included, whatever
8
+ * driver module reads every binding of the script that way, the entry's own included, whatever
9
9
  * env its class is constructed with. The entry itself reads only the env its handlers are given.
10
10
  */
11
11
  export const STORE_DISALLOW_IMPORTABLE_ENV = 'disallow_importable_env';
12
12
  /**
13
13
  * The compatibility flag that keeps `ctx.exports` off. From compatibility date 2025-11-17 it is
14
- * on by default, and the kind, which is constructed with the object's `ctx`, would get a
14
+ * on by default, and the driver, which is constructed with the object's `ctx`, would get a
15
15
  * loopback to the entry's own object class and default export: the namespace its env was
16
16
  * stripped of, by another name. Every call through it would still meet the entry's checks, but
17
- * the kind has no use for it, so the upload turns it off.
17
+ * the driver has no use for it, so the upload turns it off.
18
18
  */
19
19
  export const STORE_DISABLE_CTX_EXPORTS = 'disable_ctx_exports';
20
- /** The flags a kind release may not set, because each would undo one of the two above. */
21
- const REFUSED_KIND_FLAGS = ['allow_importable_env', 'enable_ctx_exports'];
20
+ /** The flags a driver release may not set, because each would undo one of the two above. */
21
+ const REFUSED_DRIVER_FLAGS = ['allow_importable_env', 'enable_ctx_exports'];
22
22
  /** An inline config value. The shared binding schema does not carry `text`, so it is cast once here. */
23
23
  const plainText = (name, text) => ({ type: 'plain_text', name, text });
24
24
  const encode = (text) => new TextEncoder().encode(text);
25
- /** A kind class name the entry can import: an identifier, and not the entry's own class. */
26
- function assertKindClass(className, identifier) {
25
+ /** A driver class name the entry can import: an identifier, and not the entry's own class. */
26
+ function assertDriverClass(className, identifier) {
27
27
  if (!identifier.test(className))
28
- throw new Error(`store kind class '${className}' is not an identifier`);
28
+ throw new Error(`store driver class '${className}' is not an identifier`);
29
29
  if (className === STORE_OBJECT_CLASS) {
30
- throw new Error(`store kind class '${className}' is the entry's own object class`);
30
+ throw new Error(`store driver class '${className}' is the entry's own object class`);
31
31
  }
32
32
  }
33
33
  /**
@@ -52,9 +52,9 @@ function storeEntryBindings(options) {
52
52
  }
53
53
  /**
54
54
  * The store script as uploaded (#2289). The entry becomes the main module and imports the
55
- * kind module that was the bundle's entry. The object class is added to the SQLite migration,
55
+ * driver module that was the bundle's entry. The object class is added to the SQLite migration,
56
56
  * and the script name and both audiences' public keys are bound for the entry, which checks
57
- * every call before the kind's class is constructed.
57
+ * every call before the driver's class is constructed.
58
58
  *
59
59
  * Refused, before anything is built: a class name that is not an identifier or is the entry's
60
60
  * own object class; a bundle entry that is not a plain module name; a bundle that ships a
@@ -64,40 +64,40 @@ function storeEntryBindings(options) {
64
64
  * name; a compatibility flag that would undo one of the upload's own (`allow_importable_env`,
65
65
  * `enable_ctx_exports`).
66
66
  *
67
- * The upload carries `disallow_importable_env` and `disable_ctx_exports`, so the kind reaches
67
+ * The upload carries `disallow_importable_env` and `disable_ctx_exports`, so the driver reaches
68
68
  * the entry's bindings neither through `cloudflare:workers` nor through `ctx.exports`.
69
69
  */
70
70
  export function withStoreEntry(bundle, options) {
71
- assertKindClass(options.className, /^[A-Za-z_$][A-Za-z0-9_$]*$/);
71
+ assertDriverClass(options.className, /^[A-Za-z_$][A-Za-z0-9_$]*$/);
72
72
  if (!MODULE_NAME.test(bundle.entry) || STORE_RESERVED_MODULES.includes(bundle.entry)) {
73
- throw new Error(`store kind entry '${bundle.entry}' is not a module name the store entry can import`);
73
+ throw new Error(`store driver entry '${bundle.entry}' is not a module name the store entry can import`);
74
74
  }
75
75
  const reservedModule = bundle.modules.find((module) => STORE_RESERVED_MODULES.includes(module.name));
76
76
  if (reservedModule) {
77
- throw new Error(`a store kind release may not ship a module named '${reservedModule.name}'`);
77
+ throw new Error(`a store driver release may not ship a module named '${reservedModule.name}'`);
78
78
  }
79
79
  if ((bundle.doClasses ?? []).includes(STORE_OBJECT_CLASS)) {
80
- throw new Error(`a store kind release may not declare the class '${STORE_OBJECT_CLASS}'`);
80
+ throw new Error(`a store driver release may not declare the class '${STORE_OBJECT_CLASS}'`);
81
81
  }
82
82
  const reserved = (bundle.bindings ?? []).find((binding) => binding.name.startsWith(STORE_RESERVED_BINDING_PREFIX));
83
83
  if (reserved) {
84
- throw new Error(`a store kind release may not declare the binding '${reserved.name}'`);
84
+ throw new Error(`a store driver release may not declare the binding '${reserved.name}'`);
85
85
  }
86
- const refusedFlag = (bundle.compatibilityFlags ?? []).find((flag) => REFUSED_KIND_FLAGS.includes(flag));
86
+ const refusedFlag = (bundle.compatibilityFlags ?? []).find((flag) => REFUSED_DRIVER_FLAGS.includes(flag));
87
87
  if (refusedFlag) {
88
- throw new Error(`a store kind release may not set '${refusedFlag}'`);
88
+ throw new Error(`a store driver release may not set '${refusedFlag}'`);
89
89
  }
90
90
  const entryBindings = storeEntryBindings(options);
91
- const source = STORE_ENTRY_SOURCE.split(STORE_KIND_PLACEHOLDER)
91
+ const source = STORE_ENTRY_SOURCE.split(STORE_DRIVER_PLACEHOLDER)
92
92
  .join(`./${bundle.entry}`)
93
93
  .split(STORE_CLASS_PLACEHOLDER)
94
94
  .join(options.className);
95
- const kind = 'application/javascript+module';
95
+ const driver = 'application/javascript+module';
96
96
  const bindings = [...(bundle.bindings ?? []), ...entryBindings];
97
97
  return {
98
98
  ...bundle,
99
99
  entry: STORE_ENTRY_MODULE,
100
- modules: [{ name: STORE_ENTRY_MODULE, content: encode(source), contentType: kind }, ...bundle.modules],
100
+ modules: [{ name: STORE_ENTRY_MODULE, content: encode(source), contentType: driver }, ...bundle.modules],
101
101
  doClasses: [...(bundle.doClasses ?? []), STORE_OBJECT_CLASS],
102
102
  compatibilityFlags: [
103
103
  ...new Set([...(bundle.compatibilityFlags ?? []), STORE_DISALLOW_IMPORTABLE_ENV, STORE_DISABLE_CTX_EXPORTS]),
@@ -106,32 +106,32 @@ export function withStoreEntry(bundle, options) {
106
106
  };
107
107
  }
108
108
  /**
109
- * A Python kind module's path: one top-level package that starts with `kind_`, then
110
- * identifiers, ending in `.py`. The prefix keeps a kind's modules off every name the Python
109
+ * A Python driver module's path: one top-level package that starts with `driver_`, then
110
+ * identifiers, ending in `.py`. The prefix keeps a driver's modules off every name the Python
111
111
  * entry imports or the runtime provides (`workers`, `js`, `pyodide`, the entry, its verifier),
112
112
  * and off `metadata`, the upload's own form part.
113
113
  */
114
- const PYTHON_KIND_MODULE = /^(kind_[a-z0-9_]+)(?:\/[A-Za-z_][A-Za-z0-9_]*)*\/[A-Za-z_][A-Za-z0-9_]*\.py$/;
114
+ const PYTHON_DRIVER_MODULE = /^(driver_[a-z0-9_]+)(?:\/[A-Za-z_][A-Za-z0-9_]*)*\/[A-Za-z_][A-Za-z0-9_]*\.py$/;
115
115
  /**
116
- * The store script as uploaded for a Python kind (#2290, #2210). The Python entry is the main
117
- * module, with the kind's module and class in place of its two placeholders, and the verifier
118
- * it imports is shipped beside it, so it runs the JavaScript entry's check. The kind's modules
116
+ * The store script as uploaded for a Python driver (#2290, #2210). The Python entry is the main
117
+ * module, with the driver's module and class in place of its two placeholders, and the verifier
118
+ * it imports is shipped beside it, so it runs the JavaScript entry's check. The driver's modules
119
119
  * follow, byte for byte. The bindings and the two flags are the JavaScript entry's
120
120
  * (`withStoreEntry`), with `python_workers`.
121
121
  *
122
122
  * Refused, before anything is built (D-63): a class name that is not a Python identifier or is
123
- * the entry's own class; a module that is not `kind_<package>/…/<module>.py`; modules under more
123
+ * the entry's own class; a module that is not `driver_<package>/…/<module>.py`; modules under more
124
124
  * than one package; a module named twice; an entry that is not one of the modules; and
125
125
  * whatever `withStoreEntry` refuses of the script name and the keys. A reserved module name
126
126
  * cannot pass the path rule, and is refused again by name.
127
127
  */
128
128
  export function withPythonStoreEntry(release, options) {
129
- assertKindClass(options.className, /^[A-Za-z_][A-Za-z0-9_]*$/);
130
- assertPythonKindModules(release);
129
+ assertDriverClass(options.className, /^[A-Za-z_][A-Za-z0-9_]*$/);
130
+ assertPythonDriverModules(release);
131
131
  const bindings = storeEntryBindings(options);
132
- const kindModule = release.entry.replace(/\.py$/, '').replaceAll('/', '.');
133
- const source = STORE_ENTRY_PYTHON_SOURCE.split(STORE_KIND_PLACEHOLDER)
134
- .join(kindModule)
132
+ const driverModule = release.entry.replace(/\.py$/, '').replaceAll('/', '.');
133
+ const source = STORE_ENTRY_PYTHON_SOURCE.split(STORE_DRIVER_PLACEHOLDER)
134
+ .join(driverModule)
135
135
  .split(STORE_CLASS_PLACEHOLDER)
136
136
  .join(options.className);
137
137
  return {
@@ -151,28 +151,28 @@ export function withPythonStoreEntry(release, options) {
151
151
  };
152
152
  }
153
153
  /**
154
- * The module rule of a Python kind's release (D-63): every module is `kind_<package>/…/<module>.py`
154
+ * The module rule of a Python driver's release (D-63): every module is `driver_<package>/…/<module>.py`
155
155
  * under one package, none twice, and the entry is one of them. The upload applies it when a
156
156
  * release arrives (#2300), and `withPythonStoreEntry` again before every script it builds.
157
157
  */
158
- export function assertPythonKindModules(release) {
158
+ export function assertPythonDriverModules(release) {
159
159
  const packages = new Set();
160
160
  const names = new Set();
161
161
  for (const module of release.modules) {
162
- const path = PYTHON_KIND_MODULE.exec(module.name);
162
+ const path = PYTHON_DRIVER_MODULE.exec(module.name);
163
163
  if (!path || STORE_RESERVED_MODULES.includes(module.name)) {
164
- throw new Error(`a store kind release may not ship a module named '${module.name}': a kind module is kind_<package>/…/<module>.py`);
164
+ throw new Error(`a store driver release may not ship a module named '${module.name}': a driver module is driver_<package>/…/<module>.py`);
165
165
  }
166
166
  if (names.has(module.name))
167
- throw new Error(`a store kind release ships '${module.name}' twice`);
167
+ throw new Error(`a store driver release ships '${module.name}' twice`);
168
168
  names.add(module.name);
169
169
  packages.add(path[1]);
170
170
  }
171
171
  if (packages.size > 1) {
172
- throw new Error(`a store kind release ships more than one package (${[...packages].sort().join(', ')})`);
172
+ throw new Error(`a store driver release ships more than one package (${[...packages].sort().join(', ')})`);
173
173
  }
174
174
  if (!names.has(release.entry)) {
175
- throw new Error(`store kind entry '${release.entry}' is not one of the release's modules`);
175
+ throw new Error(`store driver entry '${release.entry}' is not one of the release's modules`);
176
176
  }
177
177
  }
178
178
  //# sourceMappingURL=store-entry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-entry.js","sourceRoot":"","sources":["../src/store-entry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store-entry.js","sourceRoot":"","sources":["../src/store-entry.ts"],"names":[],"mappings":"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"}