browser-broker 0.1.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 (123) hide show
  1. package/.env.example +173 -0
  2. package/LICENSE +21 -0
  3. package/README.md +374 -0
  4. package/RELEASES.md +97 -0
  5. package/dist/package.json +58 -0
  6. package/dist/src/adapter/conformance/case.js +1 -0
  7. package/dist/src/adapter/conformance/cases.js +429 -0
  8. package/dist/src/adapter/conformance/discovery.js +156 -0
  9. package/dist/src/adapter/conformance/driver.js +1 -0
  10. package/dist/src/adapter/conformance/drivers.js +36 -0
  11. package/dist/src/adapter/conformance/run.js +224 -0
  12. package/dist/src/adapter/conformance/service-subject.js +165 -0
  13. package/dist/src/adapter/contract.js +24 -0
  14. package/dist/src/adapter/operations.js +114 -0
  15. package/dist/src/adapter/service-seam.js +1 -0
  16. package/dist/src/artifacts/names.js +229 -0
  17. package/dist/src/artifacts/store.js +174 -0
  18. package/dist/src/bin/broker-tool.js +63 -0
  19. package/dist/src/bin/broker.js +111 -0
  20. package/dist/src/browser/adoption.js +143 -0
  21. package/dist/src/browser/automation-probe.js +113 -0
  22. package/dist/src/browser/conformance/case.js +1 -0
  23. package/dist/src/browser/conformance/cases.js +192 -0
  24. package/dist/src/browser/conformance/run.js +102 -0
  25. package/dist/src/browser/conformance/subjects.js +19 -0
  26. package/dist/src/browser/discovery.js +226 -0
  27. package/dist/src/browser/driver.js +195 -0
  28. package/dist/src/browser/fake.js +585 -0
  29. package/dist/src/browser/launch.js +504 -0
  30. package/dist/src/browser/real.js +1425 -0
  31. package/dist/src/browser/setup.js +161 -0
  32. package/dist/src/capture/accounting.js +59 -0
  33. package/dist/src/capture/image.js +112 -0
  34. package/dist/src/capture/ladder.js +72 -0
  35. package/dist/src/capture/legibility.js +195 -0
  36. package/dist/src/capture/pipeline.js +153 -0
  37. package/dist/src/capture/tiers.js +166 -0
  38. package/dist/src/cli/adapter.js +233 -0
  39. package/dist/src/cli/commands.js +270 -0
  40. package/dist/src/cli/conformance-driver.js +119 -0
  41. package/dist/src/cli/diffs.js +122 -0
  42. package/dist/src/cli/image.js +274 -0
  43. package/dist/src/cli/index.js +895 -0
  44. package/dist/src/cli/login-command.js +401 -0
  45. package/dist/src/cli/operations-commands.js +186 -0
  46. package/dist/src/cli/reconcile-command.js +137 -0
  47. package/dist/src/cli/sign-in.js +134 -0
  48. package/dist/src/cli/telemetry.js +222 -0
  49. package/dist/src/config/environment.js +446 -0
  50. package/dist/src/diff/artifact-path.js +77 -0
  51. package/dist/src/diff/crops.js +102 -0
  52. package/dist/src/diff/geometry.js +122 -0
  53. package/dist/src/diff/image.js +132 -0
  54. package/dist/src/diff/mask.js +46 -0
  55. package/dist/src/diff/regions.js +263 -0
  56. package/dist/src/diff/settings.js +135 -0
  57. package/dist/src/doctor/checks.js +588 -0
  58. package/dist/src/doctor/report.js +152 -0
  59. package/dist/src/doctor/session.js +161 -0
  60. package/dist/src/errors.js +36 -0
  61. package/dist/src/feedback/read.js +119 -0
  62. package/dist/src/feedback/record.js +199 -0
  63. package/dist/src/operations/addresses.js +175 -0
  64. package/dist/src/operations/derive.js +109 -0
  65. package/dist/src/operations/ledger.js +194 -0
  66. package/dist/src/operations/status.js +197 -0
  67. package/dist/src/operations/telemetry.js +280 -0
  68. package/dist/src/report/document.js +419 -0
  69. package/dist/src/report/escape.js +68 -0
  70. package/dist/src/report/snapshot.js +97 -0
  71. package/dist/src/service/arbitration.js +537 -0
  72. package/dist/src/service/artifacts.js +85 -0
  73. package/dist/src/service/bridge.js +577 -0
  74. package/dist/src/service/broker.js +120 -0
  75. package/dist/src/service/browser-session.js +269 -0
  76. package/dist/src/service/capacity.js +62 -0
  77. package/dist/src/service/capture-seam.js +83 -0
  78. package/dist/src/service/capture-store.js +91 -0
  79. package/dist/src/service/comparison-store.js +101 -0
  80. package/dist/src/service/comparison.js +173 -0
  81. package/dist/src/service/events.js +93 -0
  82. package/dist/src/service/keys.js +68 -0
  83. package/dist/src/service/leases.js +147 -0
  84. package/dist/src/service/nudge.js +66 -0
  85. package/dist/src/service/operations/claim.js +692 -0
  86. package/dist/src/service/operations/give-back.js +131 -0
  87. package/dist/src/service/operations/pages.js +771 -0
  88. package/dist/src/service/operations/sign-in.js +915 -0
  89. package/dist/src/service/operations/status.js +62 -0
  90. package/dist/src/service/ownership.js +93 -0
  91. package/dist/src/service/pages.js +616 -0
  92. package/dist/src/service/pending-seeds.js +20 -0
  93. package/dist/src/service/queue.js +233 -0
  94. package/dist/src/service/reconcile.js +220 -0
  95. package/dist/src/service/refusals.js +262 -0
  96. package/dist/src/service/runtime.js +131 -0
  97. package/dist/src/service/signin-recovery.js +148 -0
  98. package/dist/src/service/storage-seed.js +239 -0
  99. package/dist/src/service/tabs.js +123 -0
  100. package/dist/src/store/budget.js +99 -0
  101. package/dist/src/store/location.js +42 -0
  102. package/dist/src/store/network-path.js +182 -0
  103. package/dist/src/store/network-volume.js +92 -0
  104. package/dist/src/store/open.js +226 -0
  105. package/dist/src/store/schema/step-001-initial.js +523 -0
  106. package/dist/src/store/schema/step-002-tab-budget.js +53 -0
  107. package/dist/src/store/schema/step-003-queue-order.js +110 -0
  108. package/dist/src/store/schema/step-004-tab-never-opened.js +100 -0
  109. package/dist/src/store/schema/step-005-storage-seed-event.js +90 -0
  110. package/dist/src/store/schema/step-006-signin-events.js +104 -0
  111. package/dist/src/store/schema/step-007-signin-without-process.js +92 -0
  112. package/dist/src/store/schema/step-008-signin-owner.js +76 -0
  113. package/dist/src/store/schema/step-009-named-browsers.js +138 -0
  114. package/dist/src/store/schema/step-010-signin-request.js +135 -0
  115. package/dist/src/store/schema/step.js +172 -0
  116. package/dist/src/store/schema/steps.js +58 -0
  117. package/dist/src/store/transaction.js +37 -0
  118. package/dist/src/tool/adapter.js +90 -0
  119. package/dist/src/tool/conformance-driver.js +184 -0
  120. package/dist/src/tool/protocol.js +310 -0
  121. package/dist/src/tool/session.js +351 -0
  122. package/dist/src/tool/tools.js +310 -0
  123. package/package.json +58 -0
@@ -0,0 +1,131 @@
1
+ import { updateSweptTabs, } from "../arbitration.js";
2
+ import { append } from "../events.js";
3
+ import { hashKey } from "../keys.js";
4
+ import { promoteWhileCapacity } from "../queue.js";
5
+ import { CallRefusal } from "../refusals.js";
6
+ /**
7
+ * Release whatever the lease holds.
8
+ *
9
+ * The tab to close is **collected, not closed**. §2.4b is the hard rule and
10
+ * the reason is worth restating at the site rather than only in the header:
11
+ * closing a tab is a round trip to a browser process that can hang, and a
12
+ * wedged browser does not refuse — it accepts the request and never answers.
13
+ * Inside the transaction that blocks every arbitration call on the machine,
14
+ * including every caller with no interest in that browser, because they are
15
+ * all serialised behind the same writer.
16
+ */
17
+ export function decideRelease(scope, input, settings) {
18
+ const { db, adapter, swept } = scope;
19
+ const now = swept.sweptAt;
20
+ const row = db
21
+ .prepare(`SELECT id AS claimId, session_id AS sessionId, browser_id AS browserId,
22
+ state, ended_at AS endedAt
23
+ FROM claims
24
+ WHERE key_hash = @keyHash`)
25
+ .get({ keyHash: hashKey(input.key) });
26
+ if (row === undefined) {
27
+ // The only refusal (§3.4). A key this store has never seen names nothing
28
+ // to give back, and forgiving that would mean reporting success for a
29
+ // lease that never existed.
30
+ scope.recordRefusal({
31
+ kind: 'claim_released',
32
+ outcome: 'deny',
33
+ guard: 'key.valid',
34
+ adapter,
35
+ });
36
+ throw new CallRefusal('unrecognised_key', 'That key does not match any lease in this store, so there is nothing to give back.');
37
+ }
38
+ if (row.state !== 'queued' && row.state !== 'active') {
39
+ // Forgiving: succeeds, and says the lease had already ended.
40
+ append(db, {
41
+ kind: 'claim_released',
42
+ outcome: 'allow',
43
+ adapter,
44
+ claimId: row.claimId,
45
+ sessionId: row.sessionId,
46
+ browserId: row.browserId,
47
+ detail: { alreadyEnded: true, state: row.state, endedAt: row.endedAt },
48
+ });
49
+ return {
50
+ value: {
51
+ released: 'nothing',
52
+ claimId: row.claimId,
53
+ alreadyEnded: true,
54
+ state: row.state,
55
+ // Nothing was held, so nothing is outstanding.
56
+ completeAtCommit: true,
57
+ promoted: 0,
58
+ },
59
+ };
60
+ }
61
+ const wasQueued = row.state === 'queued';
62
+ db.prepare(`UPDATE claims
63
+ SET state = 'released', ended_at = @now, updated_at = @now
64
+ WHERE id = @id`).run({ id: row.claimId, now });
65
+ // On a queued lease this finds nothing, which is the whole difference
66
+ // between the two halves: there was never a tab to collect.
67
+ const tabs = db
68
+ .prepare(`SELECT id AS tabId, claim_id AS claimId, browser_id AS browserId
69
+ FROM tabs
70
+ WHERE claim_id = @claimId AND state IN ('opening', 'open')
71
+ ORDER BY id`)
72
+ .all({ claimId: row.claimId });
73
+ // The same rule the sweep uses, from the same function, because two writers
74
+ // spelling it separately is how they come to disagree — and they did: both
75
+ // moved every tab to `closing`, which the schema refuses for a tab that
76
+ // never opened, and every tab this build creates is one of those.
77
+ //
78
+ // What comes back is the subset a browser still owes an answer about.
79
+ const pendingCloses = updateSweptTabs(db, tabs, now);
80
+ append(db, {
81
+ kind: 'claim_released',
82
+ outcome: 'allow',
83
+ adapter,
84
+ claimId: row.claimId,
85
+ sessionId: row.sessionId,
86
+ browserId: row.browserId,
87
+ detail: {
88
+ released: wasQueued ? 'queue-place' : 'tab',
89
+ tabs: tabs.length,
90
+ releasedAt: now,
91
+ },
92
+ });
93
+ for (const tab of pendingCloses) {
94
+ // Collected inside, closed outside (§2.4b). This schedules; it cannot
95
+ // call a browser, because the scope carries no driver to call one with.
96
+ scope.closeAfterCommit(tab);
97
+ append(db, {
98
+ kind: 'tab_closing',
99
+ outcome: 'allow',
100
+ adapter,
101
+ claimId: row.claimId,
102
+ tabId: tab.tabId,
103
+ sessionId: row.sessionId,
104
+ browserId: tab.browserId,
105
+ });
106
+ }
107
+ // **Everyone behind moves up immediately** (§2.5, #72). Releasing a queue
108
+ // place frees no capacity, so this promotes nobody in that case — the
109
+ // people behind move up because the position is a count of the queued
110
+ // leases ahead, and one of them just stopped being queued.
111
+ const promoted = promoteWhileCapacity(db, {
112
+ budget: settings.tabBudget,
113
+ leaseSeconds: settings.leaseSeconds,
114
+ adapter,
115
+ now,
116
+ });
117
+ return {
118
+ value: {
119
+ released: wasQueued ? 'queue-place' : 'tab',
120
+ claimId: row.claimId,
121
+ alreadyEnded: false,
122
+ state: 'released',
123
+ // A queued release is complete at commit, because there is no browser
124
+ // round trip in it — the one case where the best-effort caveat in
125
+ // §2.4b's step three does not apply and the response can say so
126
+ // without qualification.
127
+ completeAtCommit: wasQueued,
128
+ promoted: promoted.length,
129
+ },
130
+ };
131
+ }