@volter/world-core 2.0.36 → 3.0.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 (205) hide show
  1. package/README.md +4 -5
  2. package/app-route.cjs +12 -6
  3. package/app-route.d.cts +1 -1
  4. package/dist/app-route.cjs +12 -6
  5. package/dist/app-route.d.cts +1 -1
  6. package/dist/generated/pack-facts.json +1410 -3069
  7. package/dist/inject.cjs +64 -9
  8. package/dist/pack-facts.cjs +44 -0
  9. package/dist/src/actions.d.ts +3 -3
  10. package/dist/src/actions.js +22 -16
  11. package/dist/src/ancestry.d.ts +14 -2
  12. package/dist/src/ancestry.js +92 -2
  13. package/dist/src/anthropic-wire.d.ts +39 -0
  14. package/dist/src/anthropic-wire.js +136 -0
  15. package/dist/src/bytes.d.ts +7 -0
  16. package/dist/src/bytes.js +35 -0
  17. package/dist/src/changeset.d.ts +1 -1
  18. package/dist/src/changeset.js +0 -0
  19. package/dist/src/clickhouse/index.d.ts +3 -0
  20. package/dist/src/clickhouse/index.js +6 -0
  21. package/dist/src/clickhouse/sql.d.ts +233 -0
  22. package/dist/src/clickhouse/sql.js +4329 -0
  23. package/dist/src/clickhouse/types.d.ts +18 -0
  24. package/dist/src/clickhouse/types.js +47 -0
  25. package/dist/src/clickhouse/values.d.ts +146 -0
  26. package/dist/src/clickhouse/values.js +858 -0
  27. package/dist/src/client-bundle.js +2 -3
  28. package/dist/src/cors.d.ts +15 -0
  29. package/dist/src/cors.js +31 -0
  30. package/dist/src/derived-core.d.ts +487 -24
  31. package/dist/src/derived-core.js +788 -144
  32. package/dist/src/derived-real.d.ts +13 -0
  33. package/dist/src/derived-real.js +518 -0
  34. package/dist/src/derived.d.ts +35 -1
  35. package/dist/src/derived.js +61 -9
  36. package/dist/src/emit.js +1 -2
  37. package/dist/src/events.d.ts +206 -0
  38. package/dist/src/events.js +341 -0
  39. package/dist/src/executor.d.ts +3 -0
  40. package/dist/src/executor.js +19 -2
  41. package/dist/src/file-response.d.ts +6 -0
  42. package/dist/src/file-response.js +30 -0
  43. package/dist/src/fork.js +3 -2
  44. package/dist/src/git/history.d.ts +7 -0
  45. package/dist/src/git/history.js +24 -0
  46. package/dist/src/git/index.d.ts +1 -0
  47. package/dist/src/git/index.js +1 -0
  48. package/dist/src/git/lfs.d.ts +28 -0
  49. package/dist/src/git/lfs.js +66 -0
  50. package/dist/src/git/objects.js +3 -8
  51. package/dist/src/git/smart-http.d.ts +3 -1
  52. package/dist/src/git/smart-http.js +67 -6
  53. package/dist/src/graphql-wire.d.ts +29 -0
  54. package/dist/src/graphql-wire.js +101 -0
  55. package/dist/src/grpc-wire.d.ts +67 -0
  56. package/dist/src/grpc-wire.js +170 -0
  57. package/dist/src/h2.d.ts +40 -0
  58. package/dist/src/h2.js +656 -0
  59. package/dist/src/head.d.ts +32 -3
  60. package/dist/src/head.js +161 -40
  61. package/dist/src/history.d.ts +1 -1
  62. package/dist/src/history.js +6 -6
  63. package/dist/src/hpack.json +1 -0
  64. package/dist/src/index.d.ts +64 -75
  65. package/dist/src/index.js +58 -101
  66. package/dist/src/log.js +28 -19
  67. package/dist/src/machines.d.ts +50 -0
  68. package/dist/src/machines.js +151 -0
  69. package/dist/src/managed-database.d.ts +86 -0
  70. package/dist/src/managed-database.js +283 -0
  71. package/dist/src/multipart.d.ts +11 -0
  72. package/dist/src/multipart.js +51 -0
  73. package/dist/src/observe.d.ts +15 -5
  74. package/dist/src/observe.js +23 -9
  75. package/dist/src/openai-wire.d.ts +108 -0
  76. package/dist/src/openai-wire.js +337 -0
  77. package/dist/src/pack-assets.d.ts +3 -4
  78. package/dist/src/pack-assets.js +15 -10
  79. package/dist/src/pack-fetch.d.ts +77 -0
  80. package/dist/src/pack-fetch.js +449 -0
  81. package/dist/src/pack-paths.d.ts +12 -0
  82. package/dist/src/pack-paths.js +86 -0
  83. package/dist/src/packRegistry.d.ts +69 -162
  84. package/dist/src/packRegistry.js +55 -20
  85. package/dist/src/people.d.ts +13 -0
  86. package/dist/src/people.js +18 -0
  87. package/dist/src/placeholder-image.d.ts +5 -0
  88. package/dist/src/placeholder-image.js +114 -0
  89. package/dist/src/protobuf.d.ts +28 -0
  90. package/dist/src/protobuf.js +332 -0
  91. package/dist/src/redis/engine.js +1 -1
  92. package/dist/src/request-scope.d.ts +1 -1
  93. package/dist/src/request-scope.js +6 -4
  94. package/dist/src/resource-blob.d.ts +5 -0
  95. package/dist/src/resource-blob.js +11 -0
  96. package/dist/src/runtime.d.ts +85 -0
  97. package/dist/src/runtime.js +104 -0
  98. package/dist/src/s3/wire.d.ts +60 -0
  99. package/dist/src/s3/wire.js +157 -0
  100. package/dist/src/scenario.d.ts +3 -0
  101. package/dist/src/scenario.js +2 -0
  102. package/dist/src/schema-sample.d.ts +1 -0
  103. package/dist/src/schema-sample.js +21 -0
  104. package/dist/src/sealed-box.d.ts +14 -0
  105. package/dist/src/sealed-box.js +225 -0
  106. package/dist/src/serve-http.d.ts +14 -0
  107. package/dist/src/serve-http.js +27 -3
  108. package/dist/src/serve.d.ts +6 -0
  109. package/dist/src/serve.js +69 -14
  110. package/dist/src/signing.d.ts +135 -0
  111. package/dist/src/signing.js +222 -0
  112. package/dist/src/sigv4.d.ts +48 -0
  113. package/dist/src/sigv4.js +167 -0
  114. package/dist/src/smtp.d.ts +16 -0
  115. package/dist/src/smtp.js +72 -0
  116. package/dist/src/sockets.d.ts +51 -0
  117. package/dist/src/sockets.js +90 -0
  118. package/dist/src/state-system.d.ts +1 -0
  119. package/dist/src/state-system.js +1 -1
  120. package/dist/src/storage.d.ts +1 -1
  121. package/dist/src/storage.js +3 -3
  122. package/dist/src/trace-context.js +1 -1
  123. package/dist/src/twin-fetch.d.ts +0 -7
  124. package/dist/src/twin-fetch.js +0 -14
  125. package/dist/src/vendor-call.d.ts +6 -0
  126. package/dist/src/vendor-call.js +41 -0
  127. package/dist/src/world-store.js +1 -1
  128. package/dist/vendor-hosts.cjs +36 -125
  129. package/dist/vendor-hosts.d.cts +8 -0
  130. package/generated/pack-facts.json +1410 -3069
  131. package/inject.cjs +64 -9
  132. package/pack-facts.cjs +44 -0
  133. package/package.json +17 -3
  134. package/src/actions.ts +23 -16
  135. package/src/ancestry.ts +74 -2
  136. package/src/anthropic-wire.ts +137 -0
  137. package/src/bytes.ts +42 -0
  138. package/src/changeset.ts +5 -5
  139. package/src/clickhouse/index.ts +6 -0
  140. package/src/clickhouse/sql.ts +3059 -0
  141. package/src/clickhouse/types.ts +44 -0
  142. package/src/clickhouse/values.ts +697 -0
  143. package/src/client-bundle.ts +2 -3
  144. package/src/cors.ts +34 -0
  145. package/src/derived-core.ts +1013 -146
  146. package/src/derived-real.ts +434 -0
  147. package/src/derived.ts +73 -3
  148. package/src/emit.ts +1 -2
  149. package/src/events.ts +449 -0
  150. package/src/executor.ts +24 -2
  151. package/src/file-response.ts +27 -0
  152. package/src/fork.ts +3 -2
  153. package/src/git/history.ts +19 -0
  154. package/src/git/index.ts +1 -0
  155. package/src/git/lfs.ts +67 -0
  156. package/src/git/objects.ts +3 -5
  157. package/src/git/smart-http.ts +56 -6
  158. package/src/graphql-wire.ts +106 -0
  159. package/src/grpc-wire.ts +159 -0
  160. package/src/h2.ts +627 -0
  161. package/src/head.ts +132 -41
  162. package/src/history.ts +6 -6
  163. package/src/hpack.json +1 -0
  164. package/src/index.ts +82 -329
  165. package/src/log.ts +27 -18
  166. package/src/machines.ts +151 -0
  167. package/src/managed-database.ts +299 -0
  168. package/src/multipart.ts +51 -0
  169. package/src/observe.ts +31 -15
  170. package/src/openai-wire.ts +371 -0
  171. package/src/pack-assets.ts +15 -11
  172. package/src/pack-fetch.ts +458 -0
  173. package/src/pack-paths.ts +72 -0
  174. package/src/packRegistry.ts +79 -167
  175. package/src/people.ts +31 -0
  176. package/src/placeholder-image.ts +88 -0
  177. package/src/protobuf.ts +251 -0
  178. package/src/redis/engine.ts +1 -1
  179. package/src/request-scope.ts +8 -4
  180. package/src/resource-blob.ts +13 -0
  181. package/src/runtime.ts +344 -0
  182. package/src/s3/wire.ts +172 -0
  183. package/src/scenario.ts +4 -0
  184. package/src/schema-sample.ts +24 -0
  185. package/src/sealed-box.ts +182 -0
  186. package/src/serve-http.ts +31 -3
  187. package/src/serve.ts +58 -14
  188. package/src/signing.ts +231 -0
  189. package/src/sigv4.ts +158 -0
  190. package/src/smtp.ts +76 -0
  191. package/src/sockets.ts +140 -0
  192. package/src/state-system.ts +2 -2
  193. package/src/storage.ts +3 -3
  194. package/src/trace-context.ts +1 -1
  195. package/src/twin-fetch.ts +0 -20
  196. package/src/vendor-call.ts +41 -0
  197. package/src/world-store.ts +1 -1
  198. package/vendor-hosts.cjs +36 -125
  199. package/vendor-hosts.d.cts +8 -0
  200. package/dist/src/mirror-shell.d.ts +0 -2
  201. package/dist/src/mirror-shell.js +0 -13
  202. package/dist/src/v1-removed.d.ts +0 -159
  203. package/dist/src/v1-removed.js +0 -124
  204. package/src/mirror-shell.ts +0 -15
  205. package/src/v1-removed.ts +0 -172
@@ -13,124 +13,19 @@
13
13
  // path once the request is decrypted.
14
14
  // • pathname supplied ⇒ "does this vendor serve THIS path on this host?" (routing).
15
15
  //
16
- // Nearly every predicate ignores the second argument, because nearly every vendor owns its host
17
- // outright. `www.googleapis.com` is the exception: `googleauth` serves the OAuth2 token exchange
18
- // there and `youtube` serves the Data API v3 at /youtube/v3/*. Since `resolveTwin` returns the
19
- // FIRST matching vendor in insertion order, a host-only match would give whichever key is declared
20
- // first the entire host — silently breaking the other twin whenever both are configured in one
21
- // world. The two predicates below are therefore mutually exclusive on that host BY PATH.
22
- //
23
- // THE SHARED-HOST RULE (Cal.com incident, 2026-08): a shared-host predicate must claim ONLY the
24
- // paths its twin actually serves — never "the rest of the host". Claiming the remainder is how
25
- // every `@googleapis/calendar` call (all of its methods default to www.googleapis.com) was routed
26
- // into the googleauth/gemini twin and answered with a plausible Google-shaped 404: a mis-route
27
- // that fails OPEN and PLAUSIBLE, strictly worse than a coverage gap. An UNCLAIMED path on a
28
- // claimed host is refused LOUDLY instead (see unclaimedTwinnedHostPathMessage below): the error
29
- // names the host as twinned and the path as belonging to no pack in this world.
30
- /**
31
- * Does this path belong to the OAuth2 token-exchange surface the googleauth twin actually serves?
32
- * Exactly the routes gemini-twin.ts §googleauth models: `POST /token` (the oauth2.googleapis.com
33
- * path, also answered on the legacy host) and `POST /oauth2/v4/token` (the legacy
34
- * www.googleapis.com token path older google-auth-library versions default to). NOTHING else:
35
- * /calendar/v3/* (the googlecalendar pack's), /drive/v3/*, … are other Google products, and
36
- * claiming them would mis-route those SDKs into an auth twin that answers with vendor-shaped
37
- * 404s (the Cal.com incident).
38
- */
39
- // (The `googleauth` matcher itself is DATA on gemini's descriptor — packages/twin/gemini/src/index.ts.)
40
-
41
- /**
42
- * Does this path belong to Google's user-facing OAuth 2.0 / OIDC surface — the one the
43
- * `googleoauth` PACK serves?
44
- *
45
- * accounts.google.com is NOT an API host: it is Google's whole sign-in web property (the account
46
- * chooser, Gmail SSO, security settings, the Workspace admin login). Claiming the host outright
47
- * would be the Cal.com mis-route incident again in a much larger blast radius — every Google
48
- * sign-in a browser in the world performs would land in a twin that models one protocol. So this
49
- * claims EXACTLY the OAuth/OIDC paths the pack serves and refuses the rest LOUDLY
50
- * (unclaimedTwinnedHostPathMessage).
51
- */
52
- const isGoogleOAuthConsentPath = (p) =>
53
- typeof p === 'string'
54
- && (/^\/(o\/oauth2\/v2\/auth(\/oauthchooseaccount)?|o\/oauth2\/auth|o\/oauth2\/token|signin\/oauth\/error|\.well-known\/openid-configuration)\/?$/.test(p)
55
- || /^\/_twin\//.test(p));
56
- // ANCHORED, and `/o/oauth2/auth/oauthchooseaccount` deliberately absent. The first version had no
57
- // terminal anchor, so `/o/oauth2/tokeninfo`, `/signin/oauth/errorPage` and
58
- // `/.well-known/openid-configuration.json` were all claimed and all 404 — and its alternation also
59
- // claimed an UN-VERSIONED chooser path the router does not serve (§9 round two). The claim set is
60
- // now exactly `AUTH_PATHS` + the three singles + the twin-only prefix.
61
- // NB `/o/oauth2/revoke` (the legacy revocation alias) is NOT claimed: the pack files it as a todo
62
- // and its handler 404s it. An injector entry claiming a path the twin then refuses routes real
63
- // traffic into a twin that cannot serve it — the Cal.com mis-route in miniature, and the reason
64
- // every claim here is checked against what the router actually answers.
65
-
66
- /**
67
- * The oauth2.googleapis.com paths the googleoauth PACK serves: the token endpoint (all three
68
- * spellings), revocation, and token introspection.
69
- */
70
- const isGoogleOAuthTokenPath = (p) =>
71
- typeof p === 'string' && /^\/(token|oauth2\/v4\/token|revoke|tokeninfo|device\/code)\/?$/.test(p);
72
- // `/device/code` is a DELIBERATE exception to "never claim a path the twin refuses", and the two
73
- // rules genuinely collide here (§9 round two). The twin's own discovery document advertises
74
- // `device_authorization_endpoint` — because Google's does, and dropping it would be infidelity — so
75
- // a discovery-driven client WILL dial it. Unclaimed, that client escapes a sealed world and reaches
76
- // the real Google; claimed, it gets the twin's honest 404. Containment wins: an unmodelled
77
- // operation must fail LOCALLY, not succeed remotely.
78
-
79
- /**
80
- * The www.googleapis.com paths it serves: the v1 (PEM) and v3 (JWK) cert endpoints, the v3
81
- * userinfo alias, and the OAuth2 API v2's userinfo (`/oauth2/v2/userinfo` and its `/userinfo/v2/me`
82
- * alias, a different field set: `googleoauth.endpoints.userinfo_v2`), which `@googleapis/oauth2`
83
- * calls (Cal.com's Google Calendar callback). NOT `/oauth2/v2/certs`: Google publishes no v2 certs
84
- * endpoint at all.
85
- */
86
- const isGoogleOAuthApisPath = (p) =>
87
- typeof p === 'string' && /^\/(oauth2\/(v1\/certs|v3\/(certs|userinfo)|v2\/userinfo)|userinfo\/v2\/me)\/?$/.test(p);
16
+ // Where two packs share a host (www.googleapis.com: googleoauth's /oauth2/*, googlecalendar's
17
+ // /calendar/v3), each claims ONLY the paths its twin serves, never "the rest of the host": a pack
18
+ // claiming the remainder answers another product's SDK with plausible vendor-shaped 404s, a
19
+ // mis-route that fails open and plausible, strictly worse than a coverage gap. An UNCLAIMED path on
20
+ // a claimed host is refused LOUDLY instead: the error names the host as twinned and the path as
21
+ // belonging to no pack in this world.
88
22
 
89
23
  // vendor → predicate(hostname, pathname?). A vendor is only active if its twin URL is set.
90
24
  // THE HAND TABLE'S ONLY RESIDENTS: keys served by KERNEL packages (browser-assets serves
91
- // `browserassets` and `googlefavicon`; kernel packages carry no pack descriptor) and
92
- // `googleoauth`, which must precede gemini's `googleauth` key on the shared token host
93
- // (`resolveTwin` is first-match in insertion order, and this table is inserted before the
94
- // compiled descriptors below). Every vendor pack's hosts live on its descriptor as DATA —
95
- // host | suffix | hostPattern, pathPattern, exclude, key — compiled from pack-facts.json.
25
+ // `browserassets` and `googlefavicon`; kernel packages carry no pack descriptor). Every vendor
26
+ // pack's hosts live on its descriptor as DATA — host | suffix | hostPattern, pathPattern,
27
+ // exclude, key — compiled from pack-facts.json.
96
28
  const VENDOR_HOSTS = {
97
- // google-auth-library exchanges a locally-signed JWT here before ANY
98
- // Vertex/GCS call — a sealed world must model the exchange or every
99
- // service-account flow dies in auth before reaching a modeled API.
100
- //
101
- // SHARES www.googleapis.com with the `youtube` PACK (whose `hosts` declaration carries the
102
- // matching pathPattern): this vendor claims
103
- // ONLY the token-exchange paths its twin serves there (`/token`, `/oauth2/v4/token` —
104
- // gemini-twin.ts §googleauth; the `googleauth` key on gemini's descriptor). It used to claim "everything that
105
- // is not YouTube" on that host, which silently swallowed Google Calendar (`/calendar/v3/*` —
106
- // `@googleapis/calendar` defaults every method to www.googleapis.com) and answered it with
107
- // plausible Google-shaped 404s from the wrong pack: the Cal.com mis-route incident. Unclaimed
108
- // paths on this host now refuse loudly instead (unclaimedTwinnedHostPathMessage). With no
109
- // pathname it still claims the host so the ambient proxy MITMs it; the post-decrypt resolve
110
- // then routes each request to the right twin — or to the loud refusal.
111
- //
112
- // GOOGLEOAUTH — the real PACK (@volter/twin-googleoauth), declared BEFORE `googleauth` on purpose.
113
- // `resolveTwin` returns the FIRST matching vendor in insertion order, so in a world where BOTH are
114
- // configured the token endpoint goes to the pack rather than to the pack-less plumbing key — which
115
- // is what you want, because the pack's token endpoint is a strict SUPERSET: it serves
116
- // authorization_code and refresh_token (which googleauth has no state for) AND the
117
- // jwt-bearer service-account exchange googleauth exists for, with a REAL RS256 id_token instead of
118
- // gemini's `alg: none` stub. A world running ONLY the gemini twin still resolves through
119
- // `googleauth` below, unchanged.
120
- //
121
- // FOUR hosts, every one path-scoped, because Google spreads ONE product across them and shares
122
- // three of them with other products:
123
- // • accounts.google.com the consent screen, the error page, OIDC discovery. NOT claimed
124
- // outright: this host is Google's entire sign-in web property.
125
- // • oauth2.googleapis.com token / revoke / tokeninfo. SHARED with `googleauth`.
126
- // • www.googleapis.com the JWKS + legacy userinfo. SHARED with `googleauth` AND
127
- // `youtube`, and disambiguated from both BY PATH.
128
- // • openidconnect.googleapis.com the OIDC userinfo endpoint. Not shared.
129
- googleoauth: (h, p) =>
130
- (h === 'accounts.google.com' && (p === undefined || isGoogleOAuthConsentPath(p)))
131
- || (h === 'oauth2.googleapis.com' && (p === undefined || isGoogleOAuthTokenPath(p)))
132
- || (h === 'www.googleapis.com' && (p === undefined || isGoogleOAuthApisPath(p)))
133
- || h === 'openidconnect.googleapis.com',
134
29
  googlefavicon: (h) => h === 'www.google.com' || h === 'google.com' || h === 't2.gstatic.com',
135
30
  browserassets: (h) =>
136
31
  h === 'cdnjs.cloudflare.com' ||
@@ -149,23 +44,29 @@ const VENDOR_HOSTS = {
149
44
  h === 'i.ytimg.com',
150
45
  };
151
46
 
152
- // descriptor-first migration (adding-a-twin.md §3): packs now declare their hosts as DATA on their descriptor
47
+ // descriptor-first migration (architecture.md, "The descriptor"): packs now declare their hosts as DATA on their descriptor
153
48
  // (`hosts` on TwinPack), compiled into the committed pack-facts artifact — a plain JSON this
154
49
  // preloaded, dependency-free module can `require` natively. The hand table above shrinks toward
155
50
  // empty as entries move; a vendor present in both homes throws (dual declaration is the drift
156
51
  // this migration ends, and a broken checkout should refuse to inject rather than half-route).
157
- {
158
- const { packs } = require('./generated/pack-facts.json');
52
+ /** The keys each pack's compiled rules answer under, by vendor: a pack's own facts learned later (addPackHosts) replace
53
+ * that vendor's rules, never another's. */
54
+ const COMPILED = new Map();
55
+ /** The hand table's keys, before any pack's rules: a pack installed later replaces its hand entry in place (keeping its
56
+ * position, which decides who answers a shared host first), where the built-in facts may not declare one twice. */
57
+ const HAND = new Set(Object.keys(VENDOR_HOSTS));
58
+ function compilePackHosts(packs, overlay) {
159
59
  for (const vendor of Object.keys(packs)) {
160
60
  const rules = packs[vendor].hosts;
161
61
  if (!rules || rules.length === 0) continue;
162
- if (VENDOR_HOSTS[vendor]) {
62
+ for (const key of COMPILED.get(vendor) || []) delete VENDOR_HOSTS[key];
63
+ COMPILED.delete(vendor);
64
+ if (VENDOR_HOSTS[vendor] && !(overlay && HAND.has(vendor))) {
163
65
  throw new Error(`inject: vendor "${vendor}" declares hosts on its pack descriptor AND in the hand VENDOR_HOSTS table — one home per fact; delete the hand entry.`);
164
66
  }
165
- // Rules group by `key` (default: the vendor id) — an aws descriptor declares the s3 /
166
- // dynamodb / … routing identities its consolidated twin answers under; gemini declares
167
- // `googleauth`. A key matches when ANY include rule matches AND NO exclude rule matches
168
- // (upstash: `.upstash.io` minus the vector hosts).
67
+ // Rules group by `key` (default: the vendor id) — aws's descriptor declares the s3 and
68
+ // secretsmanager routing identities its twin answers under. A key matches when ANY include
69
+ // rule matches AND NO exclude rule matches.
169
70
  const byKey = new Map();
170
71
  for (const rule of rules) {
171
72
  const key = rule.key === undefined ? vendor : rule.key;
@@ -173,7 +74,7 @@ const VENDOR_HOSTS = {
173
74
  byKey.get(key).push(rule);
174
75
  }
175
76
  for (const [key, keyRules] of byKey) {
176
- if (VENDOR_HOSTS[key]) {
77
+ if (VENDOR_HOSTS[key] && !(overlay && HAND.has(key))) {
177
78
  throw new Error(`inject: key "${key}" (pack ${vendor}) is declared twice — on this descriptor and in the hand VENDOR_HOSTS table or another pack's descriptor; a key has one home.`);
178
79
  }
179
80
  const compile = (rule) => {
@@ -188,9 +89,19 @@ const VENDOR_HOSTS = {
188
89
  const includes = keyRules.filter((r) => r.exclude !== true).map(compile);
189
90
  const excludes = keyRules.filter((r) => r.exclude === true).map(compile);
190
91
  VENDOR_HOSTS[key] = (h, p) => includes.some((m) => m(h, p)) && !excludes.some((m) => m(h, p));
92
+ COMPILED.set(vendor, [...(COMPILED.get(vendor) || []), key]);
191
93
  }
192
94
  }
193
95
  }
96
+ {
97
+ const facts = require('./pack-facts.cjs').packFacts();
98
+ const installed = new Set(facts.installed || []);
99
+ compilePackHosts(Object.fromEntries(Object.entries(facts.packs).filter(([vendor]) => !installed.has(vendor))), false);
100
+ compilePackHosts(Object.fromEntries(Object.entries(facts.packs).filter(([vendor]) => installed.has(vendor))), true);
101
+ }
102
+ /** Rules from packs' facts learned after this module loaded (a runtime told the facts of the World it boots): each
103
+ * vendor's replace its earlier rules. */
104
+ function addPackHosts(packs) { compilePackHosts(packs || {}, true); }
194
105
 
195
106
  /** Whether any vendor's host rule names `host` (at the host level, whatever the path), whether or not its twin runs. */
196
107
  function isVendorHost(host) {
@@ -204,7 +115,7 @@ function vendorsOfHost(host) {
204
115
  return Object.keys(VENDOR_HOSTS).filter((vendor) => VENDOR_HOSTS[vendor](h));
205
116
  }
206
117
 
207
- /** A vendor key's env stem: `S3_TWIN_URL` is s3's; `googleauth` → GOOGLEAUTH. */
118
+ /** A vendor key's env stem: `S3_TWIN_URL` is s3's; `browserassets` → BROWSERASSETS. */
208
119
  function twinEnvStem(vendor) {
209
120
  return String(vendor).toUpperCase().replace(/[^A-Z0-9]/g, '_');
210
121
  }
@@ -230,4 +141,4 @@ function twinOrigins(env) {
230
141
  return map;
231
142
  }
232
143
 
233
- module.exports = { VENDOR_HOSTS, isVendorHost, vendorsOfHost, twinEnvStem, twinOrigins };
144
+ module.exports = { VENDOR_HOSTS, addPackHosts, isVendorHost, vendorsOfHost, twinEnvStem, twinOrigins };
@@ -0,0 +1,8 @@
1
+ /** A vendor's host rule: whether a request to `hostname` (and, where the vendor shares a host, `pathname`) is its. */
2
+ export type HostRule = (hostname: string, pathname?: string) => boolean;
3
+ export const VENDOR_HOSTS: Record<string, HostRule>;
4
+ export function isVendorHost(hostname: string): boolean;
5
+ export function vendorsOfHost(hostname: string): string[];
6
+ export function twinEnvStem(vendor: string): string;
7
+ /** The vendors whose twin `env` points at, each with its origin. */
8
+ export function twinOrigins(env: Readonly<Record<string, string | undefined>>): Record<string, string>;