@wenathlan/saddle 2.0.153 → 2.0.155

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 (307) hide show
  1. package/Dockerfile +139 -55
  2. package/README.md +58 -59
  3. package/browser.ts +482 -0
  4. package/cli.ts +274 -0
  5. package/distribution.ts +731 -0
  6. package/docker.config +29 -8
  7. package/docs/artifactavailability.md +2 -2
  8. package/docs/browser.md +18 -1
  9. package/docs/{containerplatforms.2.1.0.md → containerplatforms210.md} +1 -1
  10. package/docs/{documentmap.1.8.12.md → documentmap1812.md} +1 -1
  11. package/docs/extension.md +13 -2
  12. package/docs/libraryapi.md +1 -1
  13. package/docs/{ninety.modalities.md → ninetymodalities.md} +87 -4
  14. package/docs/{planning.ninety.md → planningninety.md} +6 -6
  15. package/docs/{releasenotes.1.8.13.md → releasenotes1813.md} +2 -2
  16. package/docs/{releasenotes.1.8.14.md → releasenotes1814.md} +1 -1
  17. package/docs/{repo.synthesis.1.8.16.md → reposynthesis1816.md} +1 -1
  18. package/docs/todo.md +2 -2
  19. package/docs/{todo.1.8.15.md → todo1815.md} +1 -1
  20. package/docs/{todo.1.8.16.md → todo1816.md} +5 -5
  21. package/execution.ts +677 -0
  22. package/foundation.ts +314 -0
  23. package/index.ts +35 -27
  24. package/isolation.ts +84 -0
  25. package/modes.ts +170 -0
  26. package/mttg.config +1 -1
  27. package/orchestrator.ts +49 -15
  28. package/package.json +90 -75
  29. package/qemu.config +2 -2
  30. package/sandbox.ts +2094 -0
  31. package/scheduler.ts +1 -1
  32. package/server.ts +52 -0
  33. package/tsconfig.json +11 -12
  34. package/virtual.ts +1105 -0
  35. package/virtualgpu.ts +1 -1
  36. package/virtualization.ts +1 -1
  37. package/workloadlaw.ts +293 -0
  38. package/dist/App.d.ts +0 -5
  39. package/dist/App.js +0 -52
  40. package/dist/Sol/Sol.d.ts +0 -3
  41. package/dist/Sol/Sol.js +0 -46
  42. package/dist/Sol/agentbrowser/agentbrowser.d.ts +0 -9
  43. package/dist/Sol/agentbrowser/agentbrowser.js +0 -121
  44. package/dist/Sol/architecture/architecture.d.ts +0 -9
  45. package/dist/Sol/architecture/architecture.js +0 -93
  46. package/dist/Sol/compute/compute.d.ts +0 -9
  47. package/dist/Sol/compute/compute.js +0 -22
  48. package/dist/Sol/console/console.d.ts +0 -15
  49. package/dist/Sol/console/console.js +0 -292
  50. package/dist/Sol/dashboard/dashboard.d.ts +0 -10
  51. package/dist/Sol/dashboard/dashboard.js +0 -181
  52. package/dist/Sol/docs/docs.d.ts +0 -9
  53. package/dist/Sol/docs/docs.js +0 -28
  54. package/dist/Sol/home/home.d.ts +0 -35
  55. package/dist/Sol/home/home.js +0 -239
  56. package/dist/Sol/integrations/integrations.d.ts +0 -9
  57. package/dist/Sol/integrations/integrations.js +0 -83
  58. package/dist/Sol/intro/intro.d.ts +0 -7
  59. package/dist/Sol/intro/intro.js +0 -102
  60. package/dist/Sol/login/login.d.ts +0 -16
  61. package/dist/Sol/login/login.js +0 -49
  62. package/dist/Sol/notfound/notfound.d.ts +0 -9
  63. package/dist/Sol/notfound/notfound.js +0 -15
  64. package/dist/Sol/onboarding/onboarding.d.ts +0 -25
  65. package/dist/Sol/onboarding/onboarding.js +0 -60
  66. package/dist/Sol/playground/playground.d.ts +0 -9
  67. package/dist/Sol/playground/playground.js +0 -127
  68. package/dist/Sol/register/register.d.ts +0 -16
  69. package/dist/Sol/register/register.js +0 -193
  70. package/dist/Sol/shell/Shell.d.ts +0 -124
  71. package/dist/Sol/shell/Shell.js +0 -335
  72. package/dist/Sol/wingets/ComputeBootladder.d.ts +0 -6
  73. package/dist/Sol/wingets/ComputeBootladder.js +0 -71
  74. package/dist/Sol/wingets/ComputeModalitycatalog.d.ts +0 -7
  75. package/dist/Sol/wingets/ComputeModalitycatalog.js +0 -71
  76. package/dist/Sol/wingets/ConsoleApibadge.d.ts +0 -7
  77. package/dist/Sol/wingets/ConsoleApibadge.js +0 -5
  78. package/dist/Sol/wingets/ConsoleEdgelaw.d.ts +0 -6
  79. package/dist/Sol/wingets/ConsoleEdgelaw.js +0 -36
  80. package/dist/Sol/wingets/ConsoleEventstimeline.d.ts +0 -19
  81. package/dist/Sol/wingets/ConsoleEventstimeline.js +0 -12
  82. package/dist/Sol/wingets/ConsoleSnapshotcontrols.d.ts +0 -8
  83. package/dist/Sol/wingets/ConsoleSnapshotcontrols.js +0 -8
  84. package/dist/Sol/wingets/ConsoleSpecpanel.d.ts +0 -21
  85. package/dist/Sol/wingets/ConsoleSpecpanel.js +0 -75
  86. package/dist/Sol/wingets/ConsoleTerminal.d.ts +0 -22
  87. package/dist/Sol/wingets/ConsoleTerminal.js +0 -77
  88. package/dist/Sol/wingets/DashboardAccountpanel.d.ts +0 -18
  89. package/dist/Sol/wingets/DashboardAccountpanel.js +0 -65
  90. package/dist/Sol/wingets/DashboardAdminpanel.d.ts +0 -6
  91. package/dist/Sol/wingets/DashboardAdminpanel.js +0 -90
  92. package/dist/Sol/wingets/DashboardAuditlog.d.ts +0 -8
  93. package/dist/Sol/wingets/DashboardAuditlog.js +0 -50
  94. package/dist/Sol/wingets/DashboardCreatesandboxform.d.ts +0 -10
  95. package/dist/Sol/wingets/DashboardCreatesandboxform.js +0 -71
  96. package/dist/Sol/wingets/DashboardEventstimeline.d.ts +0 -8
  97. package/dist/Sol/wingets/DashboardEventstimeline.js +0 -83
  98. package/dist/Sol/wingets/DashboardGlobalsandboxestable.d.ts +0 -8
  99. package/dist/Sol/wingets/DashboardGlobalsandboxestable.js +0 -57
  100. package/dist/Sol/wingets/DashboardMeshnodestable.d.ts +0 -8
  101. package/dist/Sol/wingets/DashboardMeshnodestable.js +0 -103
  102. package/dist/Sol/wingets/DashboardSandboxlist.d.ts +0 -10
  103. package/dist/Sol/wingets/DashboardSandboxlist.js +0 -163
  104. package/dist/Sol/wingets/DashboardUserstable.d.ts +0 -8
  105. package/dist/Sol/wingets/DashboardUserstable.js +0 -78
  106. package/dist/Sol/wingets/HomeEntrygate.d.ts +0 -23
  107. package/dist/Sol/wingets/HomeEntrygate.js +0 -73
  108. package/dist/Sol/wingets/HomeMetricstrip.d.ts +0 -9
  109. package/dist/Sol/wingets/HomeMetricstrip.js +0 -4
  110. package/dist/Sol/wingets/HomeRuntimediagram.d.ts +0 -1
  111. package/dist/Sol/wingets/HomeRuntimediagram.js +0 -22
  112. package/dist/Sol/wingets/LoginForm.d.ts +0 -19
  113. package/dist/Sol/wingets/LoginForm.js +0 -141
  114. package/dist/Sol/wingets/RegisterPasswordconfirm.d.ts +0 -13
  115. package/dist/Sol/wingets/RegisterPasswordconfirm.js +0 -16
  116. package/dist/Sol/wingets/RegisterPasswordmeter.d.ts +0 -19
  117. package/dist/Sol/wingets/RegisterPasswordmeter.js +0 -65
  118. package/dist/Sol/wingets/RegisterUsernamerules.d.ts +0 -11
  119. package/dist/Sol/wingets/RegisterUsernamerules.js +0 -16
  120. package/dist/Sol/wingets/index.d.ts +0 -39
  121. package/dist/Sol/wingets/index.js +0 -39
  122. package/dist/alternatives.d.ts +0 -508
  123. package/dist/alternatives.js +0 -950
  124. package/dist/api.d.ts +0 -154
  125. package/dist/api.js +0 -174
  126. package/dist/auth.d.ts +0 -268
  127. package/dist/auth.js +0 -476
  128. package/dist/automation.d.ts +0 -425
  129. package/dist/automation.js +0 -566
  130. package/dist/browser.d.ts +0 -561
  131. package/dist/browser.js +0 -1058
  132. package/dist/capacitor.config.d.ts +0 -45
  133. package/dist/capacitor.config.js +0 -48
  134. package/dist/catalog.d.ts +0 -43
  135. package/dist/catalog.js +0 -44
  136. package/dist/cli.d.ts +0 -16
  137. package/dist/cli.js +0 -168
  138. package/dist/communication.d.ts +0 -181
  139. package/dist/communication.js +0 -376
  140. package/dist/compute.d.ts +0 -1160
  141. package/dist/compute.js +0 -2272
  142. package/dist/dashboardlib.d.ts +0 -58
  143. package/dist/dashboardlib.js +0 -161
  144. package/dist/db.d.ts +0 -569
  145. package/dist/db.js +0 -1080
  146. package/dist/distribution.d.ts +0 -528
  147. package/dist/distribution.js +0 -810
  148. package/dist/drizzle.config.d.ts +0 -20
  149. package/dist/drizzle.config.js +0 -18
  150. package/dist/edgelandes.d.ts +0 -70
  151. package/dist/edgelandes.host.d.ts +0 -26
  152. package/dist/edgelandes.host.js +0 -1089
  153. package/dist/edgelandes.inproc.d.ts +0 -27
  154. package/dist/edgelandes.inproc.js +0 -952
  155. package/dist/edgelandes.js +0 -1133
  156. package/dist/edgelandes.ninety.d.ts +0 -31
  157. package/dist/edgelandes.ninety.js +0 -174
  158. package/dist/edgelandes.virtual.d.ts +0 -25
  159. package/dist/edgelandes.virtual.js +0 -629
  160. package/dist/edgeplane.d.ts +0 -114
  161. package/dist/edgeplane.js +0 -305
  162. package/dist/examplelocaljob.d.ts +0 -1
  163. package/dist/examplelocaljob.js +0 -14
  164. package/dist/execution.d.ts +0 -529
  165. package/dist/execution.js +0 -760
  166. package/dist/family.d.ts +0 -16
  167. package/dist/family.js +0 -42
  168. package/dist/familyurl.d.ts +0 -21
  169. package/dist/familyurl.js +0 -24
  170. package/dist/format.d.ts +0 -18
  171. package/dist/format.js +0 -59
  172. package/dist/foundation.d.ts +0 -172
  173. package/dist/foundation.js +0 -312
  174. package/dist/index.d.ts +0 -735
  175. package/dist/index.js +0 -1020
  176. package/dist/integration.d.ts +0 -235
  177. package/dist/integration.js +0 -228
  178. package/dist/intelligence.d.ts +0 -93
  179. package/dist/intelligence.js +0 -144
  180. package/dist/isolation.d.ts +0 -128
  181. package/dist/isolation.js +0 -95
  182. package/dist/labels.d.ts +0 -665
  183. package/dist/labels.js +0 -751
  184. package/dist/localauth.d.ts +0 -127
  185. package/dist/localauth.js +0 -413
  186. package/dist/media.d.ts +0 -343
  187. package/dist/media.js +0 -985
  188. package/dist/mesh.d.ts +0 -177
  189. package/dist/mesh.js +0 -468
  190. package/dist/modalityregistry.d.ts +0 -109
  191. package/dist/modalityregistry.js +0 -1386
  192. package/dist/modes.d.ts +0 -358
  193. package/dist/modes.js +0 -133
  194. package/dist/onboardingstate.d.ts +0 -79
  195. package/dist/onboardingstate.js +0 -282
  196. package/dist/orchestrator.d.ts +0 -1338
  197. package/dist/orchestrator.js +0 -4310
  198. package/dist/packager/targetcli.js +0 -4
  199. package/dist/paths.d.ts +0 -2
  200. package/dist/paths.js +0 -14
  201. package/dist/performance.d.ts +0 -415
  202. package/dist/performance.js +0 -826
  203. package/dist/quantum.d.ts +0 -809
  204. package/dist/quantum.js +0 -2072
  205. package/dist/render.d.ts +0 -380
  206. package/dist/render.js +0 -1072
  207. package/dist/runall.d.ts +0 -2
  208. package/dist/runall.js +0 -39
  209. package/dist/runtimeprobe.d.ts +0 -4
  210. package/dist/runtimeprobe.js +0 -8
  211. package/dist/sandbox.d.ts +0 -355
  212. package/dist/sandbox.js +0 -1721
  213. package/dist/sandboxenvprofile.d.ts +0 -29
  214. package/dist/sandboxenvprofile.js +0 -89
  215. package/dist/sandboxprofile.d.ts +0 -137
  216. package/dist/sandboxprofile.js +0 -241
  217. package/dist/sandboxqueue.d.ts +0 -108
  218. package/dist/sandboxqueue.js +0 -201
  219. package/dist/sandboxquota.d.ts +0 -121
  220. package/dist/sandboxquota.js +0 -236
  221. package/dist/scheduler.d.ts +0 -595
  222. package/dist/scheduler.js +0 -1258
  223. package/dist/scriptlanes.d.ts +0 -182
  224. package/dist/scriptlanes.js +0 -660
  225. package/dist/scripts.buildengine.d.ts +0 -1
  226. package/dist/scripts.buildengine.js +0 -39
  227. package/dist/scripts.securitygate.d.ts +0 -1
  228. package/dist/scripts.securitygate.js +0 -68
  229. package/dist/scripts.validateflatnative.d.ts +0 -1
  230. package/dist/scripts.validateflatnative.js +0 -72
  231. package/dist/security.d.ts +0 -500
  232. package/dist/security.js +0 -1169
  233. package/dist/seedcatalog.d.ts +0 -13
  234. package/dist/seedcatalog.js +0 -46
  235. package/dist/server.d.ts +0 -20
  236. package/dist/server.js +0 -53
  237. package/dist/speccatalog.d.ts +0 -31
  238. package/dist/speccatalog.js +0 -163
  239. package/dist/tiers.d.ts +0 -1001
  240. package/dist/tiers.js +0 -2169
  241. package/dist/utils.d.ts +0 -2
  242. package/dist/utils.js +0 -5
  243. package/dist/vaulthandoff.d.ts +0 -141
  244. package/dist/vaulthandoff.js +0 -444
  245. package/dist/virtual.d.ts +0 -640
  246. package/dist/virtual.js +0 -1284
  247. package/dist/virtualcpu.d.ts +0 -315
  248. package/dist/virtualcpu.js +0 -1180
  249. package/dist/virtualgpu.d.ts +0 -344
  250. package/dist/virtualgpu.js +0 -1000
  251. package/dist/virtualization.d.ts +0 -606
  252. package/dist/virtualization.js +0 -1186
  253. package/dist/virtualmemory.d.ts +0 -817
  254. package/dist/virtualmemory.js +0 -1795
  255. package/dist/vite.config.d.ts +0 -3
  256. package/dist/vite.config.js +0 -43
  257. package/dist/vitest.config.d.ts +0 -2
  258. package/dist/vitest.config.js +0 -25
  259. package/dist/webserver.d.ts +0 -166
  260. package/dist/webserver.js +0 -2226
  261. package/dist/workloadlaw.d.ts +0 -133
  262. package/dist/workloadlaw.js +0 -193
  263. package/manifest.json +0 -20
  264. package/media.ts +0 -1337
  265. /package/docs/{architecture.1.8.19.md → architecture1819.md} +0 -0
  266. /package/docs/{architecture.1.8.19.mmd → architecture1819.mmd} +0 -0
  267. /package/docs/{artifactresearch.1.8.9.md → artifactresearch189.md} +0 -0
  268. /package/docs/{batches.1816.to.1820.md → batches1816to1820.md} +0 -0
  269. /package/docs/{brancharchive.2026.08.18.md → brancharchive20260818.md} +0 -0
  270. /package/docs/{containerplatforms.1.8.17.md → containerplatforms1817.md} +0 -0
  271. /package/docs/{e2ugh.engine.md → e2ughengine.md} +0 -0
  272. /package/docs/{flatbuildresearch.1.8.11.md → flatbuildresearch1811.md} +0 -0
  273. /package/docs/{licenseaudit.1.8.12.md → licenseaudit1812.md} +0 -0
  274. /package/docs/{mobileconversionresearch.1.8.10.md → mobileconversionresearch1810.md} +0 -0
  275. /package/docs/{nativeidentityresearch.1.8.11.md → nativeidentityresearch1811.md} +0 -0
  276. /package/docs/{native.wrappers.md → nativewrappers.md} +0 -0
  277. /package/docs/{planning.1.8.18.md → planning1818.md} +0 -0
  278. /package/docs/{planning.1.8.19.md → planning1819.md} +0 -0
  279. /package/docs/{planning.engine.md → planningengine.md} +0 -0
  280. /package/docs/{releasenotes.1.8.12.md → releasenotes1812.md} +0 -0
  281. /package/docs/{releasenotes.1.8.15.md → releasenotes1815.md} +0 -0
  282. /package/docs/{releasenotes.1.8.16.md → releasenotes1816.md} +0 -0
  283. /package/docs/{releasenotes.1.8.17.md → releasenotes1817.md} +0 -0
  284. /package/docs/{releasenotes.1.8.18.md → releasenotes1818.md} +0 -0
  285. /package/docs/{releasenotes.1.8.19.md → releasenotes1819.md} +0 -0
  286. /package/docs/{reorganization.1.8.8.md → reorganization188.md} +0 -0
  287. /package/docs/{reorganization.1.8.9.md → reorganization189.md} +0 -0
  288. /package/docs/{repo.analysis.1.8.21.json → repoanalysis1821.json} +0 -0
  289. /package/docs/{repo.candidates.1.8.21.jsonl → repocandidates1821.jsonl} +0 -0
  290. /package/docs/{repo.research.1.8.16.md → reporesearch1816.md} +0 -0
  291. /package/docs/{repo.research.1.8.21.md → reporesearch1821.md} +0 -0
  292. /package/docs/{repo.selected.1.8.21.json → reposelected1821.json} +0 -0
  293. /package/docs/{repo.synthesis.1.8.21.md → reposynthesis1821.md} +0 -0
  294. /package/docs/{research.1.8.18.isolation.md → research1818isolation.md} +0 -0
  295. /package/docs/{research.1.8.19.virtual.browser.md → research1819virtualbrowser.md} +0 -0
  296. /package/docs/{scanningresearch.1.8.11.md → scanningresearch1811.md} +0 -0
  297. /package/docs/{securityaudit.1.8.7.md → securityaudit187.md} +0 -0
  298. /package/docs/{securityreferences.1.8.11.md → securityreferences1811.md} +0 -0
  299. /package/docs/{securityresearch.1.8.11.md → securityresearch1811.md} +0 -0
  300. /package/docs/{signingresearch.1.8.12.md → signingresearch1812.md} +0 -0
  301. /package/docs/{tauri.glib.risk.1.8.11.md → tauriglibrisk1811.md} +0 -0
  302. /package/docs/{todo.1.8.18.md → todo1818.md} +0 -0
  303. /package/docs/{todo.1.8.19.md → todo1819.md} +0 -0
  304. /package/docs/{toolchainresearch.1.8.9.md → toolchainresearch189.md} +0 -0
  305. /package/docs/{web.duplicatecleanup.md → webduplicatecleanup.md} +0 -0
  306. /package/docs/{web.ideas.md → webideas.md} +0 -0
  307. /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
- }