@wenathlan/saddle 2.0.154 → 2.0.159

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 (314) hide show
  1. package/Dockerfile +167 -1334
  2. package/README.md +59 -60
  3. package/biome.jsonc +3 -3
  4. package/browser.ts +482 -0
  5. package/cli.ts +274 -0
  6. package/distribution.ts +731 -0
  7. package/docker.config +29 -8
  8. package/docs/{architecture.1.8.19.mmd → architecture1819.md} +6 -0
  9. package/docs/artifactavailability.md +2 -2
  10. package/docs/browser.md +18 -1
  11. package/docs/{containerplatforms.2.1.0.md → containerplatforms210.md} +1 -1
  12. package/docs/{documentmap.1.8.12.md → documentmap1812.md} +1 -1
  13. package/docs/extension.md +13 -2
  14. package/docs/features.md +5 -0
  15. package/docs/libraryapi.md +1 -1
  16. package/docs/{ninety.modalities.md → ninetymodalities.md} +87 -4
  17. package/docs/{planning.ninety.md → planningninety.md} +6 -6
  18. package/docs/{releasenotes.1.8.13.md → releasenotes1813.md} +2 -2
  19. package/docs/{releasenotes.1.8.14.md → releasenotes1814.md} +1 -1
  20. package/docs/{repo.synthesis.1.8.16.md → reposynthesis1816.md} +1 -1
  21. package/docs/todo.md +2 -2
  22. package/docs/{todo.1.8.15.md → todo1815.md} +1 -1
  23. package/docs/{todo.1.8.16.md → todo1816.md} +5 -5
  24. package/execution.ts +677 -0
  25. package/foundation.ts +314 -0
  26. package/index.ts +36 -34
  27. package/isolation.ts +84 -0
  28. package/modalityregistry.ts +189 -145
  29. package/modes.ts +170 -0
  30. package/mttg.config +1 -1
  31. package/orchestrator.ts +49 -15
  32. package/package.json +105 -89
  33. package/qemu.config +2 -2
  34. package/sandbox.ts +3279 -0
  35. package/scheduler.ts +1 -1
  36. package/server.ts +52 -0
  37. package/tsconfig.json +12 -13
  38. package/virtual.ts +7528 -0
  39. package/workloadlaw.ts +293 -0
  40. package/dist/App.d.ts +0 -5
  41. package/dist/App.js +0 -52
  42. package/dist/Sol/Sol.d.ts +0 -3
  43. package/dist/Sol/Sol.js +0 -46
  44. package/dist/Sol/agentbrowser/agentbrowser.d.ts +0 -9
  45. package/dist/Sol/agentbrowser/agentbrowser.js +0 -121
  46. package/dist/Sol/architecture/architecture.d.ts +0 -9
  47. package/dist/Sol/architecture/architecture.js +0 -93
  48. package/dist/Sol/compute/compute.d.ts +0 -9
  49. package/dist/Sol/compute/compute.js +0 -22
  50. package/dist/Sol/console/console.d.ts +0 -15
  51. package/dist/Sol/console/console.js +0 -292
  52. package/dist/Sol/dashboard/dashboard.d.ts +0 -10
  53. package/dist/Sol/dashboard/dashboard.js +0 -181
  54. package/dist/Sol/docs/docs.d.ts +0 -9
  55. package/dist/Sol/docs/docs.js +0 -28
  56. package/dist/Sol/home/home.d.ts +0 -35
  57. package/dist/Sol/home/home.js +0 -239
  58. package/dist/Sol/integrations/integrations.d.ts +0 -9
  59. package/dist/Sol/integrations/integrations.js +0 -83
  60. package/dist/Sol/intro/intro.d.ts +0 -7
  61. package/dist/Sol/intro/intro.js +0 -102
  62. package/dist/Sol/login/login.d.ts +0 -16
  63. package/dist/Sol/login/login.js +0 -49
  64. package/dist/Sol/notfound/notfound.d.ts +0 -9
  65. package/dist/Sol/notfound/notfound.js +0 -15
  66. package/dist/Sol/onboarding/onboarding.d.ts +0 -25
  67. package/dist/Sol/onboarding/onboarding.js +0 -60
  68. package/dist/Sol/playground/playground.d.ts +0 -9
  69. package/dist/Sol/playground/playground.js +0 -127
  70. package/dist/Sol/register/register.d.ts +0 -16
  71. package/dist/Sol/register/register.js +0 -193
  72. package/dist/Sol/shell/Shell.d.ts +0 -124
  73. package/dist/Sol/shell/Shell.js +0 -335
  74. package/dist/Sol/wingets/ComputeBootladder.d.ts +0 -6
  75. package/dist/Sol/wingets/ComputeBootladder.js +0 -71
  76. package/dist/Sol/wingets/ComputeModalitycatalog.d.ts +0 -7
  77. package/dist/Sol/wingets/ComputeModalitycatalog.js +0 -71
  78. package/dist/Sol/wingets/ConsoleApibadge.d.ts +0 -7
  79. package/dist/Sol/wingets/ConsoleApibadge.js +0 -5
  80. package/dist/Sol/wingets/ConsoleEdgelaw.d.ts +0 -6
  81. package/dist/Sol/wingets/ConsoleEdgelaw.js +0 -36
  82. package/dist/Sol/wingets/ConsoleEventstimeline.d.ts +0 -19
  83. package/dist/Sol/wingets/ConsoleEventstimeline.js +0 -12
  84. package/dist/Sol/wingets/ConsoleSnapshotcontrols.d.ts +0 -8
  85. package/dist/Sol/wingets/ConsoleSnapshotcontrols.js +0 -8
  86. package/dist/Sol/wingets/ConsoleSpecpanel.d.ts +0 -21
  87. package/dist/Sol/wingets/ConsoleSpecpanel.js +0 -75
  88. package/dist/Sol/wingets/ConsoleTerminal.d.ts +0 -22
  89. package/dist/Sol/wingets/ConsoleTerminal.js +0 -77
  90. package/dist/Sol/wingets/DashboardAccountpanel.d.ts +0 -18
  91. package/dist/Sol/wingets/DashboardAccountpanel.js +0 -65
  92. package/dist/Sol/wingets/DashboardAdminpanel.d.ts +0 -6
  93. package/dist/Sol/wingets/DashboardAdminpanel.js +0 -90
  94. package/dist/Sol/wingets/DashboardAuditlog.d.ts +0 -8
  95. package/dist/Sol/wingets/DashboardAuditlog.js +0 -50
  96. package/dist/Sol/wingets/DashboardCreatesandboxform.d.ts +0 -10
  97. package/dist/Sol/wingets/DashboardCreatesandboxform.js +0 -71
  98. package/dist/Sol/wingets/DashboardEventstimeline.d.ts +0 -8
  99. package/dist/Sol/wingets/DashboardEventstimeline.js +0 -83
  100. package/dist/Sol/wingets/DashboardGlobalsandboxestable.d.ts +0 -8
  101. package/dist/Sol/wingets/DashboardGlobalsandboxestable.js +0 -57
  102. package/dist/Sol/wingets/DashboardMeshnodestable.d.ts +0 -8
  103. package/dist/Sol/wingets/DashboardMeshnodestable.js +0 -103
  104. package/dist/Sol/wingets/DashboardSandboxlist.d.ts +0 -10
  105. package/dist/Sol/wingets/DashboardSandboxlist.js +0 -163
  106. package/dist/Sol/wingets/DashboardUserstable.d.ts +0 -8
  107. package/dist/Sol/wingets/DashboardUserstable.js +0 -78
  108. package/dist/Sol/wingets/HomeEntrygate.d.ts +0 -23
  109. package/dist/Sol/wingets/HomeEntrygate.js +0 -73
  110. package/dist/Sol/wingets/HomeMetricstrip.d.ts +0 -9
  111. package/dist/Sol/wingets/HomeMetricstrip.js +0 -4
  112. package/dist/Sol/wingets/HomeRuntimediagram.d.ts +0 -1
  113. package/dist/Sol/wingets/HomeRuntimediagram.js +0 -22
  114. package/dist/Sol/wingets/LoginForm.d.ts +0 -19
  115. package/dist/Sol/wingets/LoginForm.js +0 -141
  116. package/dist/Sol/wingets/RegisterPasswordconfirm.d.ts +0 -13
  117. package/dist/Sol/wingets/RegisterPasswordconfirm.js +0 -16
  118. package/dist/Sol/wingets/RegisterPasswordmeter.d.ts +0 -19
  119. package/dist/Sol/wingets/RegisterPasswordmeter.js +0 -65
  120. package/dist/Sol/wingets/RegisterUsernamerules.d.ts +0 -11
  121. package/dist/Sol/wingets/RegisterUsernamerules.js +0 -16
  122. package/dist/Sol/wingets/index.d.ts +0 -39
  123. package/dist/Sol/wingets/index.js +0 -39
  124. package/dist/alternatives.d.ts +0 -508
  125. package/dist/alternatives.js +0 -950
  126. package/dist/api.d.ts +0 -154
  127. package/dist/api.js +0 -174
  128. package/dist/auth.d.ts +0 -268
  129. package/dist/auth.js +0 -476
  130. package/dist/automation.d.ts +0 -425
  131. package/dist/automation.js +0 -566
  132. package/dist/browser.d.ts +0 -561
  133. package/dist/browser.js +0 -1058
  134. package/dist/capacitor.config.d.ts +0 -45
  135. package/dist/capacitor.config.js +0 -48
  136. package/dist/catalog.d.ts +0 -43
  137. package/dist/catalog.js +0 -44
  138. package/dist/cli.d.ts +0 -16
  139. package/dist/cli.js +0 -168
  140. package/dist/communication.d.ts +0 -181
  141. package/dist/communication.js +0 -376
  142. package/dist/compute.d.ts +0 -1160
  143. package/dist/compute.js +0 -2272
  144. package/dist/dashboardlib.d.ts +0 -58
  145. package/dist/dashboardlib.js +0 -161
  146. package/dist/db.d.ts +0 -569
  147. package/dist/db.js +0 -1080
  148. package/dist/distribution.d.ts +0 -528
  149. package/dist/distribution.js +0 -810
  150. package/dist/drizzle.config.d.ts +0 -20
  151. package/dist/drizzle.config.js +0 -18
  152. package/dist/edgelandes.d.ts +0 -70
  153. package/dist/edgelandes.host.d.ts +0 -26
  154. package/dist/edgelandes.host.js +0 -1089
  155. package/dist/edgelandes.inproc.d.ts +0 -27
  156. package/dist/edgelandes.inproc.js +0 -952
  157. package/dist/edgelandes.js +0 -1133
  158. package/dist/edgelandes.ninety.d.ts +0 -31
  159. package/dist/edgelandes.ninety.js +0 -174
  160. package/dist/edgelandes.virtual.d.ts +0 -25
  161. package/dist/edgelandes.virtual.js +0 -629
  162. package/dist/edgeplane.d.ts +0 -121
  163. package/dist/edgeplane.js +0 -341
  164. package/dist/examplelocaljob.d.ts +0 -1
  165. package/dist/examplelocaljob.js +0 -14
  166. package/dist/execution.d.ts +0 -529
  167. package/dist/execution.js +0 -760
  168. package/dist/family.d.ts +0 -16
  169. package/dist/family.js +0 -42
  170. package/dist/familyurl.d.ts +0 -21
  171. package/dist/familyurl.js +0 -24
  172. package/dist/format.d.ts +0 -18
  173. package/dist/format.js +0 -59
  174. package/dist/foundation.d.ts +0 -172
  175. package/dist/foundation.js +0 -312
  176. package/dist/index.d.ts +0 -735
  177. package/dist/index.js +0 -1020
  178. package/dist/integration.d.ts +0 -235
  179. package/dist/integration.js +0 -228
  180. package/dist/intelligence.d.ts +0 -93
  181. package/dist/intelligence.js +0 -144
  182. package/dist/isolation.d.ts +0 -128
  183. package/dist/isolation.js +0 -95
  184. package/dist/labels.d.ts +0 -665
  185. package/dist/labels.js +0 -707
  186. package/dist/localauth.d.ts +0 -127
  187. package/dist/localauth.js +0 -413
  188. package/dist/media.d.ts +0 -343
  189. package/dist/media.js +0 -985
  190. package/dist/mesh.d.ts +0 -177
  191. package/dist/mesh.js +0 -468
  192. package/dist/modalityregistry.d.ts +0 -109
  193. package/dist/modalityregistry.js +0 -1386
  194. package/dist/modes.d.ts +0 -358
  195. package/dist/modes.js +0 -133
  196. package/dist/onboardingstate.d.ts +0 -79
  197. package/dist/onboardingstate.js +0 -282
  198. package/dist/orchestrator.d.ts +0 -1338
  199. package/dist/orchestrator.js +0 -4310
  200. package/dist/packager/targetcli.js +0 -4
  201. package/dist/paths.d.ts +0 -2
  202. package/dist/paths.js +0 -14
  203. package/dist/performance.d.ts +0 -415
  204. package/dist/performance.js +0 -826
  205. package/dist/quantum.d.ts +0 -809
  206. package/dist/quantum.js +0 -2072
  207. package/dist/render.d.ts +0 -380
  208. package/dist/render.js +0 -1072
  209. package/dist/runall.d.ts +0 -2
  210. package/dist/runall.js +0 -39
  211. package/dist/runtimeprobe.d.ts +0 -4
  212. package/dist/runtimeprobe.js +0 -8
  213. package/dist/sandbox.d.ts +0 -355
  214. package/dist/sandbox.js +0 -1721
  215. package/dist/sandboxenvprofile.d.ts +0 -29
  216. package/dist/sandboxenvprofile.js +0 -89
  217. package/dist/sandboxprofile.d.ts +0 -137
  218. package/dist/sandboxprofile.js +0 -241
  219. package/dist/sandboxqueue.d.ts +0 -108
  220. package/dist/sandboxqueue.js +0 -201
  221. package/dist/sandboxquota.d.ts +0 -121
  222. package/dist/sandboxquota.js +0 -236
  223. package/dist/scheduler.d.ts +0 -595
  224. package/dist/scheduler.js +0 -1258
  225. package/dist/scriptlanes.d.ts +0 -182
  226. package/dist/scriptlanes.js +0 -660
  227. package/dist/scripts.buildengine.d.ts +0 -1
  228. package/dist/scripts.buildengine.js +0 -39
  229. package/dist/scripts.securitygate.d.ts +0 -1
  230. package/dist/scripts.securitygate.js +0 -68
  231. package/dist/scripts.validateflatnative.d.ts +0 -1
  232. package/dist/scripts.validateflatnative.js +0 -72
  233. package/dist/security.d.ts +0 -500
  234. package/dist/security.js +0 -1169
  235. package/dist/seedcatalog.d.ts +0 -13
  236. package/dist/seedcatalog.js +0 -46
  237. package/dist/server.d.ts +0 -20
  238. package/dist/server.js +0 -53
  239. package/dist/speccatalog.d.ts +0 -31
  240. package/dist/speccatalog.js +0 -163
  241. package/dist/tiers.d.ts +0 -1001
  242. package/dist/tiers.js +0 -2169
  243. package/dist/utils.d.ts +0 -2
  244. package/dist/utils.js +0 -5
  245. package/dist/vaulthandoff.d.ts +0 -141
  246. package/dist/vaulthandoff.js +0 -444
  247. package/dist/virtual.d.ts +0 -640
  248. package/dist/virtual.js +0 -1284
  249. package/dist/virtualcpu.d.ts +0 -315
  250. package/dist/virtualcpu.js +0 -1180
  251. package/dist/virtualgpu.d.ts +0 -344
  252. package/dist/virtualgpu.js +0 -1000
  253. package/dist/virtualization.d.ts +0 -606
  254. package/dist/virtualization.js +0 -1186
  255. package/dist/virtualmemory.d.ts +0 -817
  256. package/dist/virtualmemory.js +0 -1795
  257. package/dist/vite.config.d.ts +0 -3
  258. package/dist/vite.config.js +0 -48
  259. package/dist/vitest.config.d.ts +0 -2
  260. package/dist/vitest.config.js +0 -25
  261. package/dist/webserver.d.ts +0 -166
  262. package/dist/webserver.js +0 -2226
  263. package/dist/workloadlaw.d.ts +0 -133
  264. package/dist/workloadlaw.js +0 -193
  265. package/docs/.gitkeep +0 -0
  266. package/docs/architecture.1.8.19.md +0 -76
  267. package/docs/logs/examplesession.json +0 -54
  268. package/docs/repo.analysis.1.8.21.json +0 -2617
  269. package/docs/repo.candidates.1.8.21.jsonl +0 -666
  270. package/docs/repo.selected.1.8.21.json +0 -786
  271. package/manifest.json +0 -20
  272. package/media.ts +0 -1337
  273. package/virtualcpu.ts +0 -1274
  274. package/virtualgpu.ts +0 -1182
  275. package/virtualization.ts +0 -1628
  276. package/virtualmemory.ts +0 -2303
  277. /package/docs/{artifactresearch.1.8.9.md → artifactresearch189.md} +0 -0
  278. /package/docs/{batches.1816.to.1820.md → batches1816to1820.md} +0 -0
  279. /package/docs/{brancharchive.2026.08.18.md → brancharchive20260818.md} +0 -0
  280. /package/docs/{containerplatforms.1.8.17.md → containerplatforms1817.md} +0 -0
  281. /package/docs/{e2ugh.engine.md → e2ughengine.md} +0 -0
  282. /package/docs/{flatbuildresearch.1.8.11.md → flatbuildresearch1811.md} +0 -0
  283. /package/docs/{licenseaudit.1.8.12.md → licenseaudit1812.md} +0 -0
  284. /package/docs/{mobileconversionresearch.1.8.10.md → mobileconversionresearch1810.md} +0 -0
  285. /package/docs/{nativeidentityresearch.1.8.11.md → nativeidentityresearch1811.md} +0 -0
  286. /package/docs/{native.wrappers.md → nativewrappers.md} +0 -0
  287. /package/docs/{planning.1.8.18.md → planning1818.md} +0 -0
  288. /package/docs/{planning.1.8.19.md → planning1819.md} +0 -0
  289. /package/docs/{planning.engine.md → planningengine.md} +0 -0
  290. /package/docs/{releasenotes.1.8.12.md → releasenotes1812.md} +0 -0
  291. /package/docs/{releasenotes.1.8.15.md → releasenotes1815.md} +0 -0
  292. /package/docs/{releasenotes.1.8.16.md → releasenotes1816.md} +0 -0
  293. /package/docs/{releasenotes.1.8.17.md → releasenotes1817.md} +0 -0
  294. /package/docs/{releasenotes.1.8.18.md → releasenotes1818.md} +0 -0
  295. /package/docs/{releasenotes.1.8.19.md → releasenotes1819.md} +0 -0
  296. /package/docs/{reorganization.1.8.8.md → reorganization188.md} +0 -0
  297. /package/docs/{reorganization.1.8.9.md → reorganization189.md} +0 -0
  298. /package/docs/{repo.research.1.8.16.md → reporesearch1816.md} +0 -0
  299. /package/docs/{repo.research.1.8.21.md → reporesearch1821.md} +0 -0
  300. /package/docs/{repo.synthesis.1.8.21.md → reposynthesis1821.md} +0 -0
  301. /package/docs/{research.1.8.18.isolation.md → research1818isolation.md} +0 -0
  302. /package/docs/{research.1.8.19.virtual.browser.md → research1819virtualbrowser.md} +0 -0
  303. /package/docs/{scanningresearch.1.8.11.md → scanningresearch1811.md} +0 -0
  304. /package/docs/{securityaudit.1.8.7.md → securityaudit187.md} +0 -0
  305. /package/docs/{securityreferences.1.8.11.md → securityreferences1811.md} +0 -0
  306. /package/docs/{securityresearch.1.8.11.md → securityresearch1811.md} +0 -0
  307. /package/docs/{signingresearch.1.8.12.md → signingresearch1812.md} +0 -0
  308. /package/docs/{tauri.glib.risk.1.8.11.md → tauriglibrisk1811.md} +0 -0
  309. /package/docs/{todo.1.8.18.md → todo1818.md} +0 -0
  310. /package/docs/{todo.1.8.19.md → todo1819.md} +0 -0
  311. /package/docs/{toolchainresearch.1.8.9.md → toolchainresearch189.md} +0 -0
  312. /package/docs/{web.duplicatecleanup.md → webduplicatecleanup.md} +0 -0
  313. /package/docs/{web.ideas.md → webideas.md} +0 -0
  314. /package/docs/{workflowimprovements.2026.08.19.md → workflowimprovements20260819.md} +0 -0
@@ -1,282 +0,0 @@
1
- /**
2
- * onboardingstate.ts — the ONE writer of the visitor's first-run flags, the
3
- * state of the entry-flow contract (intro → onboarding → the platform). The
4
- * state is { introseen, onboardingseen } and it is catalogued, not just
5
- * browser: the accessor consults the platform database over GET
6
- * /api/v1/onboarding and writes it over POST /api/v1/onboarding, both HTTPS
7
- * only — the base url arrives through VITE_ONBOARDING_URL and every
8
- * candidate host is validated first (localhost, loopback, private,
9
- * link-local, CGNAT and the reserved documentation ranges are refused, so
10
- * the flags never ride a dev or internal endpoint; mapped IPv6 literals are
11
- * normalized back to their embedded IPv4 before the public check). The
12
- * localStorage key saddle.onboarding.seen is purely the offline cache of
13
- * the database (the DB is the source of truth; the cache avoids replaying
14
- * the flow offline) and module memory covers client-side re-navigation.
15
- * Reads reconcile with a seen-wins merge — a stage seen anywhere stays
16
- * seen, the flow is never forced twice. The page anchors never touch the
17
- * flags directly: the intro marks introseen, the onboarding marks
18
- * onboardingseen, the home entry gate only reads.
19
- */
20
- /** the localStorage cache key (the DB mirror, never the source of truth). */
21
- const CACHE_KEY = 'saddle.onboarding.seen';
22
- /** the localStorage key of the stable per-browser visitor id. */
23
- const VISITOR_KEY = 'saddle.visitor.id';
24
- /** the configured platform base (HTTPS self-hosted gateway); empty answers cache-only. */
25
- const BASE = (typeof import.meta !== 'undefined'
26
- ? import.meta.env?.VITE_ONBOARDING_URL
27
- : undefined)?.trim().replace(/\/+$/, '') ?? '';
28
- /** the platform route the accessor rides (the gateway serves /api/v1/*). */
29
- const API_PATH = '/api/v1/onboarding';
30
- /** the abort window of the platform read (ms) — the flow never blocks on a slow gateway. */
31
- const READ_TIMEOUT_MS = 2500;
32
- /** module memory: the flags of the session, consulted before any i/o. */
33
- let memory = null;
34
- /**
35
- * Validates a platform base url: HTTPS only, and the host must be public —
36
- * localhost, loopback, private, link-local, CGNAT, benchmarking,
37
- * unique-local and the reserved documentation ranges are refused; a mapped
38
- * IPv6 literal is normalized to its embedded IPv4 first; any other IPv6
39
- * literal is refused (the gateway rides a name).
40
- *
41
- * @param base the configured base url candidate.
42
- * @returns true when the base may be consulted.
43
- */
44
- export function hostallowed(base) {
45
- try {
46
- const url = new URL(base);
47
- if (url.protocol !== 'https:')
48
- return false;
49
- const host = url.hostname
50
- .toLowerCase()
51
- .replace(/\.+$/, '')
52
- .replace(/^\[|\]$/g, '');
53
- if (host.length === 0)
54
- return false;
55
- if (host === 'localhost' || host === '0.0.0.0' || host === '::1' || host === '::')
56
- return false;
57
- if (host.endsWith('.localhost') || host.endsWith('.local') || host.endsWith('.internal')) {
58
- return false;
59
- }
60
- if (host.endsWith('.home.arpa'))
61
- return false;
62
- if (host.includes(':'))
63
- return mappedipv6(host);
64
- const v4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
65
- if (v4)
66
- return publicipv4(Number(v4[1]), Number(v4[2]));
67
- return true;
68
- }
69
- catch {
70
- return false;
71
- }
72
- }
73
- /**
74
- * Answers whether an IPv6 literal is the IPv4-mapped form of a PUBLIC
75
- * address (the url parser normalizes `::ffff:127.0.0.1` to hex hextets, so
76
- * both spellings are normalized here and the embedded IPv4 decides).
77
- *
78
- * @param host the bracket-free ipv6 literal.
79
- * @returns true when the literal maps to a public ipv4 address.
80
- */
81
- function mappedipv6(host) {
82
- if (!host.startsWith('::ffff:'))
83
- return false;
84
- const tail = host.slice('::ffff:'.length);
85
- const decimals = tail.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
86
- if (decimals)
87
- return publicipv4(Number(decimals[1]), Number(decimals[2]));
88
- // the hex spelling the url parser produces: the first hextet of the pair
89
- // carries the first two ipv4 octets, and they alone decide the ranges
90
- const hextets = tail.match(/^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
91
- if (hextets) {
92
- const high = Number.parseInt(hextets[1], 16);
93
- return publicipv4((high >> 8) & 0xff, high & 0xff);
94
- }
95
- return false;
96
- }
97
- /** the public side of an ipv4 address (the first two octets decide). */
98
- function publicipv4(a, b) {
99
- if (a === 0 || a === 10 || a === 127)
100
- return false;
101
- if (a === 172 && b >= 16 && b <= 31)
102
- return false;
103
- if (a === 192 && b === 168)
104
- return false;
105
- if (a === 169 && b === 254)
106
- return false;
107
- if (a === 100 && b >= 64 && b <= 127)
108
- return false;
109
- if (a === 192 && b === 0)
110
- return false;
111
- if (a === 198 && (b === 18 || b === 19))
112
- return false;
113
- if (a === 198 && (b === 51 || b === 52))
114
- return false;
115
- if (a === 203 && b === 0)
116
- return false;
117
- if (a >= 224)
118
- return false;
119
- return true;
120
- }
121
- /**
122
- * Answers the flags after one stage was marked seen (pure: the writer and
123
- * the tests share this merge — seen-wins, a stage never unsees).
124
- *
125
- * @param state the flags before the write.
126
- * @param stage the stage being marked seen.
127
- * @returns the flags after the write.
128
- */
129
- export function mergeStage(state, stage) {
130
- return stage === 'intro'
131
- ? { ...state, introseen: true }
132
- : { ...state, introseen: true, onboardingseen: true };
133
- }
134
- /** Reads the localStorage cache; any corruption answers null (the cache is best effort). */
135
- function readCache() {
136
- if (typeof window === 'undefined')
137
- return null;
138
- try {
139
- const raw = window.localStorage.getItem(CACHE_KEY);
140
- if (!raw)
141
- return null;
142
- const parsed = JSON.parse(raw);
143
- return { introseen: parsed.introseen === true, onboardingseen: parsed.onboardingseen === true };
144
- }
145
- catch {
146
- return null;
147
- }
148
- }
149
- /** Mirrors the flags into the localStorage cache (silently skipped when storage is sealed). */
150
- function writeCache(state) {
151
- if (typeof window === 'undefined')
152
- return;
153
- try {
154
- window.localStorage.setItem(CACHE_KEY, JSON.stringify(state));
155
- }
156
- catch {
157
- // the cache is best effort: the DB and the memory stay authoritative
158
- }
159
- }
160
- /**
161
- * The stable per-browser visitor id (generated once, cached beside the
162
- * flags); storage unavailable answers the shared web key.
163
- *
164
- * @returns the visitor string the gateway rows are keyed by.
165
- */
166
- function visitor() {
167
- if (typeof window === 'undefined')
168
- return 'web';
169
- try {
170
- const existing = window.localStorage.getItem(VISITOR_KEY);
171
- if (existing)
172
- return existing;
173
- const fresh = crypto.randomUUID();
174
- window.localStorage.setItem(VISITOR_KEY, fresh);
175
- return fresh;
176
- }
177
- catch {
178
- return 'web';
179
- }
180
- }
181
- /** Reads the flags off the platform database; unreachable or unconfigured answers null. */
182
- async function readRemote() {
183
- if (!BASE || !hostallowed(BASE) || typeof fetch === 'undefined')
184
- return null;
185
- const controller = new AbortController();
186
- const timer = setTimeout(() => controller.abort(), READ_TIMEOUT_MS);
187
- try {
188
- const response = await fetch(`${BASE}${API_PATH}?visitor=${encodeURIComponent(visitor())}`, {
189
- headers: { accept: 'application/json' },
190
- signal: controller.signal,
191
- });
192
- if (!response.ok)
193
- return null;
194
- const answer = (await response.json());
195
- return { introseen: answer.introseen === true, onboardingseen: answer.onboardingseen === true };
196
- }
197
- catch {
198
- return null;
199
- }
200
- finally {
201
- clearTimeout(timer);
202
- }
203
- }
204
- /** Writes the flags to the platform database over POST; failures leave the cache holding them. */
205
- async function writeRemote(state) {
206
- if (!BASE || !hostallowed(BASE) || typeof fetch === 'undefined')
207
- return;
208
- try {
209
- await fetch(`${BASE}${API_PATH}`, {
210
- method: 'POST',
211
- headers: { 'content-type': 'application/json' },
212
- body: JSON.stringify({ visitor: visitor(), ...state }),
213
- });
214
- }
215
- catch {
216
- // the cache mirrors the flags until the platform answers again
217
- }
218
- }
219
- /**
220
- * Synchronous read of the known state: module memory first, then the
221
- * localStorage cache. The entry gate uses this for the flash-free fast
222
- * path; null means the state is still unknown and the caller must await
223
- * loadOnboardingState.
224
- *
225
- * @returns the known flags, or null when nothing is known yet.
226
- */
227
- export function readOnboardingState() {
228
- if (memory)
229
- return memory;
230
- const cached = readCache();
231
- if (cached) {
232
- memory = cached;
233
- return cached;
234
- }
235
- return null;
236
- }
237
- /**
238
- * Full resolve of the flags: memory/cache first, then the platform database
239
- * over GET /api/v1/onboarding — reconciled with the seen-wins merge so a
240
- * stage seen anywhere stays seen. The session answer is memoized;
241
- * re-navigation never refetches.
242
- *
243
- * @returns the resolved visitor onboarding flags.
244
- */
245
- export async function loadOnboardingState() {
246
- const local = readOnboardingState() ?? { introseen: false, onboardingseen: false };
247
- if (!BASE || !hostallowed(BASE) || (local.introseen && local.onboardingseen))
248
- return local;
249
- const remote = await readRemote();
250
- if (!remote)
251
- return local;
252
- const merged = {
253
- introseen: local.introseen || remote.introseen,
254
- onboardingseen: local.onboardingseen || remote.onboardingseen,
255
- };
256
- memory = merged;
257
- writeCache(merged);
258
- return merged;
259
- }
260
- /**
261
- * Marks the intro stage seen and persists it everywhere: module memory, the
262
- * localStorage cache and — when a platform base is configured — the
263
- * database over POST /api/v1/onboarding. The ONLY writer of the intro flag.
264
- */
265
- export function markIntroSeen() {
266
- memory = mergeStage(memory ?? readCache() ?? { introseen: false, onboardingseen: false }, 'intro');
267
- writeCache(memory);
268
- void writeRemote(memory);
269
- }
270
- /**
271
- * Marks the onboarding stage seen (finish or skip — both imply the intro)
272
- * and persists it everywhere. The ONLY writer of the onboarding flag.
273
- */
274
- export function markOnboardingSeen() {
275
- memory = mergeStage(memory ?? readCache() ?? { introseen: false, onboardingseen: false }, 'onboarding');
276
- writeCache(memory);
277
- void writeRemote(memory);
278
- }
279
- /** Test seam: clears the module memory (the cache and the platform are untouched). */
280
- export function resetOnboardingMemory() {
281
- memory = null;
282
- }