@spooky-sync/core 0.0.1-canary.24 → 0.0.1-canary.240
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.
- package/AGENTS.md +58 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +476 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/boot/auth-flip.saga.ts.html +271 -0
- package/coverage/lcov-report/src/boot/boot.saga.ts.html +469 -0
- package/coverage/lcov-report/src/boot/bucket-switch.saga.ts.html +430 -0
- package/coverage/lcov-report/src/boot/index.html +161 -0
- package/coverage/lcov-report/src/boot/preload.saga.ts.html +178 -0
- package/coverage/lcov-report/src/client/bucket-handle.ts.html +766 -0
- package/coverage/lcov-report/src/client/index.html +161 -0
- package/coverage/lcov-report/src/client/router.ts.html +367 -0
- package/coverage/lcov-report/src/client/runtime.ts.html +940 -0
- package/coverage/lcov-report/src/client/sp00ky-client.ts.html +1531 -0
- package/coverage/lcov-report/src/events/index.html +116 -0
- package/coverage/lcov-report/src/events/index.ts.html +877 -0
- package/coverage/lcov-report/src/index.html +146 -0
- package/coverage/lcov-report/src/index.ts.html +202 -0
- package/coverage/lcov-report/src/kernel/constants.ts.html +247 -0
- package/coverage/lcov-report/src/kernel/effects.ts.html +562 -0
- package/coverage/lcov-report/src/kernel/events.ts.html +250 -0
- package/coverage/lcov-report/src/kernel/index.html +176 -0
- package/coverage/lcov-report/src/kernel/interpreter.ts.html +514 -0
- package/coverage/lcov-report/src/kernel/saga.ts.html +304 -0
- package/coverage/lcov-report/src/modules/app-release/index.html +116 -0
- package/coverage/lcov-report/src/modules/app-release/index.ts.html +712 -0
- package/coverage/lcov-report/src/modules/auth/events/index.html +116 -0
- package/coverage/lcov-report/src/modules/auth/events/index.ts.html +142 -0
- package/coverage/lcov-report/src/modules/auth/index.html +116 -0
- package/coverage/lcov-report/src/modules/auth/index.ts.html +1225 -0
- package/coverage/lcov-report/src/modules/crdt/crdt-field.ts.html +967 -0
- package/coverage/lcov-report/src/modules/crdt/index.html +146 -0
- package/coverage/lcov-report/src/modules/crdt/index.ts.html +1474 -0
- package/coverage/lcov-report/src/modules/crdt/loro-loader.ts.html +160 -0
- package/coverage/lcov-report/src/modules/devtools/flags.ts.html +1132 -0
- package/coverage/lcov-report/src/modules/devtools/index.html +161 -0
- package/coverage/lcov-report/src/modules/devtools/index.ts.html +2413 -0
- package/coverage/lcov-report/src/modules/devtools/storage-info.ts.html +589 -0
- package/coverage/lcov-report/src/modules/devtools/versions.ts.html +415 -0
- package/coverage/lcov-report/src/modules/feature-flag/index.html +116 -0
- package/coverage/lcov-report/src/modules/feature-flag/index.ts.html +1033 -0
- package/coverage/lcov-report/src/modules/index.html +116 -0
- package/coverage/lcov-report/src/modules/ref-tables.ts.html +349 -0
- package/coverage/lcov-report/src/mutation/index.html +191 -0
- package/coverage/lcov-report/src/mutation/jobs.ts.html +208 -0
- package/coverage/lcov-report/src/mutation/mutation-id.ts.html +190 -0
- package/coverage/lcov-report/src/mutation/push.saga.ts.html +730 -0
- package/coverage/lcov-report/src/mutation/rows.ts.html +1216 -0
- package/coverage/lcov-report/src/mutation/tray.saga.ts.html +229 -0
- package/coverage/lcov-report/src/mutation/write.saga.ts.html +583 -0
- package/coverage/lcov-report/src/otel/index.html +116 -0
- package/coverage/lcov-report/src/otel/index.ts.html +466 -0
- package/coverage/lcov-report/src/query/env.ts.html +253 -0
- package/coverage/lcov-report/src/query/fetch.saga.ts.html +412 -0
- package/coverage/lcov-report/src/query/hash.ts.html +139 -0
- package/coverage/lcov-report/src/query/index.html +281 -0
- package/coverage/lcov-report/src/query/lifecycle.saga.ts.html +388 -0
- package/coverage/lcov-report/src/query/materialize.saga.ts.html +310 -0
- package/coverage/lcov-report/src/query/materialize.ts.html +217 -0
- package/coverage/lcov-report/src/query/membership.saga.ts.html +709 -0
- package/coverage/lcov-report/src/query/membership.ts.html +559 -0
- package/coverage/lcov-report/src/query/register.saga.ts.html +655 -0
- package/coverage/lcov-report/src/query/render-set.ts.html +265 -0
- package/coverage/lcov-report/src/query/sql.ts.html +322 -0
- package/coverage/lcov-report/src/query/window-query.ts.html +670 -0
- package/coverage/lcov-report/src/services/blobs/blob-cache.ts.html +1894 -0
- package/coverage/lcov-report/src/services/blobs/blob-manifest.ts.html +766 -0
- package/coverage/lcov-report/src/services/blobs/blob-store.ts.html +1162 -0
- package/coverage/lcov-report/src/services/blobs/index.html +161 -0
- package/coverage/lcov-report/src/services/blobs/index.ts.html +295 -0
- package/coverage/lcov-report/src/services/database/cache-engine.ts.html +664 -0
- package/coverage/lcov-report/src/services/database/connection-supervisor.ts.html +1330 -0
- package/coverage/lcov-report/src/services/database/database.ts.html +658 -0
- package/coverage/lcov-report/src/services/database/engine-factory.ts.html +184 -0
- package/coverage/lcov-report/src/services/database/errors.ts.html +187 -0
- package/coverage/lcov-report/src/services/database/events/index.html +116 -0
- package/coverage/lcov-report/src/services/database/events/index.ts.html +184 -0
- package/coverage/lcov-report/src/services/database/index.html +386 -0
- package/coverage/lcov-report/src/services/database/index.ts.html +121 -0
- package/coverage/lcov-report/src/services/database/local-migrator.ts.html +703 -0
- package/coverage/lcov-report/src/services/database/local.ts.html +1633 -0
- package/coverage/lcov-report/src/services/database/plan-render.ts.html +409 -0
- package/coverage/lcov-report/src/services/database/relation-resolver.ts.html +682 -0
- package/coverage/lcov-report/src/services/database/remote.ts.html +1036 -0
- package/coverage/lcov-report/src/services/database/sqlite-cache-engine.ts.html +4132 -0
- package/coverage/lcov-report/src/services/database/sqlite-lock-verify.ts.html +220 -0
- package/coverage/lcov-report/src/services/database/sqlite-open.ts.html +577 -0
- package/coverage/lcov-report/src/services/database/sqlite-plan-sql.ts.html +499 -0
- package/coverage/lcov-report/src/services/database/sqlite-select.ts.html +478 -0
- package/coverage/lcov-report/src/services/database/sqlite-transport.ts.html +757 -0
- package/coverage/lcov-report/src/services/database/surql-translate.ts.html +1333 -0
- package/coverage/lcov-report/src/services/database/surreal-cache-engine.ts.html +568 -0
- package/coverage/lcov-report/src/services/logger/index.html +116 -0
- package/coverage/lcov-report/src/services/logger/index.ts.html +154 -0
- package/coverage/lcov-report/src/services/persistence/index.html +146 -0
- package/coverage/lcov-report/src/services/persistence/localstorage.ts.html +163 -0
- package/coverage/lcov-report/src/services/persistence/resilient.ts.html +208 -0
- package/coverage/lcov-report/src/services/persistence/surrealdb.ts.html +271 -0
- package/coverage/lcov-report/src/services/stream-processor/index.html +146 -0
- package/coverage/lcov-report/src/services/stream-processor/index.ts.html +3313 -0
- package/coverage/lcov-report/src/services/stream-processor/permissions.ts.html +244 -0
- package/coverage/lcov-report/src/services/stream-processor/wasm-types.ts.html +346 -0
- package/coverage/lcov-report/src/services/tabs/broker-client.ts.html +934 -0
- package/coverage/lcov-report/src/services/tabs/coordinator.ts.html +1984 -0
- package/coverage/lcov-report/src/services/tabs/index.html +191 -0
- package/coverage/lcov-report/src/services/tabs/leader-locks.ts.html +310 -0
- package/coverage/lcov-report/src/services/tabs/protocol.ts.html +871 -0
- package/coverage/lcov-report/src/services/tabs/support.ts.html +193 -0
- package/coverage/lcov-report/src/services/tabs/tabs-broker-worker.ts.html +2005 -0
- package/coverage/lcov-report/src/sp00ky.ts.html +100 -0
- package/coverage/lcov-report/src/state/client-state.ts.html +637 -0
- package/coverage/lcov-report/src/state/index.html +161 -0
- package/coverage/lcov-report/src/state/lifecycle.ts.html +343 -0
- package/coverage/lcov-report/src/state/reducers.ts.html +1471 -0
- package/coverage/lcov-report/src/state/selectors.ts.html +637 -0
- package/coverage/lcov-report/src/sync/connection.saga.ts.html +343 -0
- package/coverage/lcov-report/src/sync/index.html +176 -0
- package/coverage/lcov-report/src/sync/live.saga.ts.html +232 -0
- package/coverage/lcov-report/src/sync/policy.ts.html +1606 -0
- package/coverage/lcov-report/src/sync/poll.saga.ts.html +193 -0
- package/coverage/lcov-report/src/sync/tabs.saga.ts.html +361 -0
- package/coverage/lcov-report/src/types.ts.html +2419 -0
- package/coverage/lcov-report/src/utils/blurhash.ts.html +355 -0
- package/coverage/lcov-report/src/utils/error-classification.ts.html +244 -0
- package/coverage/lcov-report/src/utils/index.html +191 -0
- package/coverage/lcov-report/src/utils/index.ts.html +820 -0
- package/coverage/lcov-report/src/utils/parser.ts.html +313 -0
- package/coverage/lcov-report/src/utils/semver.ts.html +175 -0
- package/coverage/lcov-report/src/utils/surql.ts.html +667 -0
- package/coverage/lcov.info +19079 -0
- package/dist/index.d.ts +2946 -140
- package/dist/index.js +13424 -2356
- package/dist/otel/index.d.ts +2 -2
- package/dist/otel/index.js +6 -6
- package/dist/sqlite-open.js +322 -0
- package/dist/sqlite-worker.d.ts +1 -0
- package/dist/sqlite-worker.js +439 -0
- package/dist/tabs-broker-worker.d.ts +8 -0
- package/dist/tabs-broker-worker.js +472 -0
- package/dist/types.d.ts +769 -11
- package/package.json +11 -7
- package/scripts/check-broker-bundle.mjs +33 -0
- package/skills/{spooky-core → sp00ky-core}/SKILL.md +12 -12
- package/skills/{spooky-core → sp00ky-core}/references/auth.md +1 -1
- package/skills/{spooky-core → sp00ky-core}/references/config.md +2 -2
- package/src/bucket-blurhash.test.ts +148 -0
- package/src/build-globals.d.ts +12 -0
- package/src/events/events.test.ts +2 -1
- package/src/events/index.ts +3 -0
- package/src/index.ts +36 -2
- package/src/modules/app-release/index.test.ts +125 -0
- package/src/modules/app-release/index.ts +201 -0
- package/src/modules/auth/auth.local-first.test.ts +141 -0
- package/src/modules/auth/events/index.ts +2 -1
- package/src/modules/auth/index.ts +138 -25
- package/src/modules/cache/cache.relay.test.ts +95 -0
- package/src/modules/cache/index.ts +163 -43
- package/src/modules/cache/types.ts +2 -2
- package/src/modules/crdt/crdt-field.ts +294 -0
- package/src/modules/crdt/crdt-hydration.test.ts +210 -0
- package/src/modules/crdt/crdt-reconnect.test.ts +195 -0
- package/src/modules/crdt/index.ts +463 -0
- package/src/modules/crdt/loro-loader.ts +25 -0
- package/src/modules/data/data.activity.test.ts +109 -0
- package/src/modules/data/data.authority.test.ts +122 -0
- package/src/modules/data/data.heartbeat-wake.test.ts +115 -0
- package/src/modules/data/data.hydration.test.ts +142 -0
- package/src/modules/data/data.membership.test.ts +623 -0
- package/src/modules/data/data.notify-table.test.ts +41 -0
- package/src/modules/data/data.pending-ids.test.ts +199 -0
- package/src/modules/data/data.rebind.test.ts +170 -0
- package/src/modules/data/data.rematerialize.test.ts +114 -0
- package/src/modules/data/data.run.test.ts +113 -0
- package/src/modules/data/data.settled-writes.test.ts +235 -0
- package/src/modules/data/data.status.test.ts +249 -0
- package/src/modules/data/id-set-plan.test.ts +122 -0
- package/src/modules/data/index.ts +2031 -150
- package/src/modules/data/mutation-id.test.ts +25 -0
- package/src/modules/data/mutation-id.ts +35 -0
- package/src/modules/data/window-query.test.ts +52 -0
- package/src/modules/data/window-query.ts +194 -0
- package/src/modules/devtools/flags.ts +349 -0
- package/src/modules/devtools/index.ts +451 -46
- package/src/modules/devtools/notify-throttle.test.ts +154 -0
- package/src/modules/devtools/state-shape.test.ts +146 -0
- package/src/modules/devtools/storage-info.test.ts +79 -0
- package/src/modules/devtools/storage-info.ts +168 -0
- package/src/modules/devtools/versions.test.ts +74 -0
- package/src/modules/devtools/versions.ts +110 -0
- package/src/modules/feature-flag/index.test.ts +251 -0
- package/src/modules/feature-flag/index.ts +308 -0
- package/src/modules/ref-tables.test.ts +91 -0
- package/src/modules/ref-tables.ts +88 -0
- package/src/modules/sync/engine.ts +170 -86
- package/src/modules/sync/events/index.ts +9 -2
- package/src/modules/sync/queue/queue-down.test.ts +228 -0
- package/src/modules/sync/queue/queue-down.ts +108 -13
- package/src/modules/sync/queue/queue-up.forwarded.test.ts +164 -0
- package/src/modules/sync/queue/queue-up.ts +241 -57
- package/src/modules/sync/scheduler.pause.test.ts +109 -0
- package/src/modules/sync/scheduler.retry.test.ts +237 -0
- package/src/modules/sync/scheduler.ts +215 -13
- package/src/modules/sync/sync.cleanup.test.ts +116 -0
- package/src/modules/sync/sync.health.test.ts +167 -0
- package/src/modules/sync/sync.heartbeat.test.ts +80 -0
- package/src/modules/sync/sync.list-ref-meta.test.ts +193 -0
- package/src/modules/sync/sync.live-removal.test.ts +289 -0
- package/src/modules/sync/sync.reconnect.test.ts +145 -0
- package/src/modules/sync/sync.rematerialize-after-fetch.test.ts +93 -0
- package/src/modules/sync/sync.settled-poll.test.ts +58 -0
- package/src/modules/sync/sync.subquery.test.ts +82 -0
- package/src/modules/sync/sync.tabs.test.ts +249 -0
- package/src/modules/sync/sync.ts +2350 -133
- package/src/modules/sync/utils.test.ts +322 -2
- package/src/modules/sync/utils.ts +286 -16
- package/src/otel/index.ts +13 -10
- package/src/services/blobs/blob-cache.test.ts +359 -0
- package/src/services/blobs/blob-cache.ts +603 -0
- package/src/services/blobs/blob-manifest.ts +227 -0
- package/src/services/blobs/blob-store.test.ts +77 -0
- package/src/services/blobs/blob-store.ts +359 -0
- package/src/services/blobs/blob.fixture.ts +90 -0
- package/src/services/blobs/index.ts +70 -0
- package/src/services/database/cache-engine.ts +193 -0
- package/src/services/database/connection-supervisor.test.ts +289 -0
- package/src/services/database/connection-supervisor.ts +415 -0
- package/src/services/database/database.query-timeout.test.ts +140 -0
- package/src/services/database/database.ts +130 -62
- package/src/services/database/engine-factory.ts +33 -0
- package/src/services/database/errors.ts +34 -0
- package/src/services/database/events/index.ts +2 -1
- package/src/services/database/index.ts +7 -0
- package/src/services/database/local-migrator.ts +30 -27
- package/src/services/database/local.test.ts +64 -0
- package/src/services/database/local.ts +484 -67
- package/src/services/database/plan-render.test.ts +159 -0
- package/src/services/database/plan-render.ts +108 -0
- package/src/services/database/relation-resolver.test.ts +467 -0
- package/src/services/database/relation-resolver.ts +0 -0
- package/src/services/database/remote.ts +222 -15
- package/src/services/database/sqlite-cache-engine.test.ts +616 -0
- package/src/services/database/sqlite-cache-engine.timeout.test.ts +61 -0
- package/src/services/database/sqlite-cache-engine.ts +1358 -0
- package/src/services/database/sqlite-devtools-queries.integration.test.ts +143 -0
- package/src/services/database/sqlite-devtools-queries.test.ts +154 -0
- package/src/services/database/sqlite-lock-verify.test.ts +33 -0
- package/src/services/database/sqlite-lock-verify.ts +45 -0
- package/src/services/database/sqlite-open.test.ts +150 -0
- package/src/services/database/sqlite-open.ts +164 -0
- package/src/services/database/sqlite-plan-sql.test.ts +104 -0
- package/src/services/database/sqlite-plan-sql.ts +138 -0
- package/src/services/database/sqlite-projection.test.ts +99 -0
- package/src/services/database/sqlite-select.integration.test.ts +185 -0
- package/src/services/database/sqlite-select.test.ts +246 -0
- package/src/services/database/sqlite-select.ts +131 -0
- package/src/services/database/sqlite-transport.fixture.ts +30 -0
- package/src/services/database/sqlite-transport.ts +224 -0
- package/src/services/database/sqlite-worker.ts +437 -0
- package/src/services/database/surql-translate.ts +416 -0
- package/src/services/database/surreal-cache-engine.ts +161 -0
- package/src/services/logger/index.ts +3 -2
- package/src/services/persistence/localstorage.ts +2 -2
- package/src/services/persistence/resilient.ts +11 -4
- package/src/services/persistence/surrealdb.ts +10 -10
- package/src/services/stream-processor/index.ts +796 -84
- package/src/services/stream-processor/permissions.test.ts +47 -0
- package/src/services/stream-processor/permissions.ts +53 -0
- package/src/services/stream-processor/stream-processor.batch.test.ts +186 -0
- package/src/services/stream-processor/stream-processor.prime.test.ts +198 -0
- package/src/services/stream-processor/stream-processor.reset.test.ts +226 -0
- package/src/services/stream-processor/stream-processor.test.ts +1 -1
- package/src/services/stream-processor/wasm-types.ts +59 -3
- package/src/services/tabs/broker-client.ts +283 -0
- package/src/services/tabs/broker.test.ts +327 -0
- package/src/services/tabs/coordinator.test.ts +365 -0
- package/src/services/tabs/coordinator.ts +633 -0
- package/src/services/tabs/fake-ports.fixture.ts +112 -0
- package/src/services/tabs/leader-locks.ts +75 -0
- package/src/services/tabs/protocol.ts +258 -0
- package/src/services/tabs/support.ts +36 -0
- package/src/services/tabs/tabs-broker-worker.ts +640 -0
- package/src/sp00ky.auth-order.test.ts +112 -0
- package/src/sp00ky.init-query.test.ts +183 -0
- package/src/sp00ky.local-first.test.ts +60 -0
- package/src/sp00ky.ts +1854 -0
- package/src/types.ts +548 -13
- package/src/utils/blurhash.ts +90 -0
- package/src/utils/error-classification.test.ts +54 -0
- package/src/utils/error-classification.ts +25 -0
- package/src/utils/index.ts +79 -13
- package/src/utils/parser.test.ts +49 -120
- package/src/utils/parser.ts +32 -2
- package/src/utils/semver.test.ts +32 -0
- package/src/utils/semver.ts +30 -0
- package/src/utils/surql.ts +30 -18
- package/src/utils/withRetry.test.ts +1 -1
- package/tsdown.config.ts +86 -1
- package/src/spooky.ts +0 -395
|
@@ -0,0 +1,1984 @@
|
|
|
1
|
+
|
|
2
|
+
<!doctype html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
|
|
5
|
+
<head>
|
|
6
|
+
<title>Code coverage report for src/services/tabs/coordinator.ts</title>
|
|
7
|
+
<meta charset="utf-8" />
|
|
8
|
+
<link rel="stylesheet" href="../../../prettify.css" />
|
|
9
|
+
<link rel="stylesheet" href="../../../base.css" />
|
|
10
|
+
<link rel="shortcut icon" type="image/x-icon" href="../../../favicon.png" />
|
|
11
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
12
|
+
<style type='text/css'>
|
|
13
|
+
.coverage-summary .sorter {
|
|
14
|
+
background-image: url(../../../sort-arrow-sprite.png);
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
17
|
+
</head>
|
|
18
|
+
|
|
19
|
+
<body>
|
|
20
|
+
<div class='wrapper'>
|
|
21
|
+
<div class='pad1'>
|
|
22
|
+
<h1><a href="../../../index.html">All files</a> / <a href="index.html">src/services/tabs</a> coordinator.ts</h1>
|
|
23
|
+
<div class='clearfix'>
|
|
24
|
+
|
|
25
|
+
<div class='fl pad1y space-right2'>
|
|
26
|
+
<span class="strong">82.65% </span>
|
|
27
|
+
<span class="quiet">Statements</span>
|
|
28
|
+
<span class='fraction'>367/444</span>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<div class='fl pad1y space-right2'>
|
|
33
|
+
<span class="strong">75.89% </span>
|
|
34
|
+
<span class="quiet">Branches</span>
|
|
35
|
+
<span class='fraction'>85/112</span>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
<div class='fl pad1y space-right2'>
|
|
40
|
+
<span class="strong">78.18% </span>
|
|
41
|
+
<span class="quiet">Functions</span>
|
|
42
|
+
<span class='fraction'>43/55</span>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<div class='fl pad1y space-right2'>
|
|
47
|
+
<span class="strong">82.65% </span>
|
|
48
|
+
<span class="quiet">Lines</span>
|
|
49
|
+
<span class='fraction'>367/444</span>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
<p class="quiet">
|
|
55
|
+
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
|
56
|
+
</p>
|
|
57
|
+
<template id="filterTemplate">
|
|
58
|
+
<div class="quiet">
|
|
59
|
+
Filter:
|
|
60
|
+
<input type="search" id="fileSearch">
|
|
61
|
+
</div>
|
|
62
|
+
</template>
|
|
63
|
+
</div>
|
|
64
|
+
<div class='status-line high'></div>
|
|
65
|
+
<pre><table class="coverage">
|
|
66
|
+
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
|
67
|
+
<a name='L2'></a><a href='#L2'>2</a>
|
|
68
|
+
<a name='L3'></a><a href='#L3'>3</a>
|
|
69
|
+
<a name='L4'></a><a href='#L4'>4</a>
|
|
70
|
+
<a name='L5'></a><a href='#L5'>5</a>
|
|
71
|
+
<a name='L6'></a><a href='#L6'>6</a>
|
|
72
|
+
<a name='L7'></a><a href='#L7'>7</a>
|
|
73
|
+
<a name='L8'></a><a href='#L8'>8</a>
|
|
74
|
+
<a name='L9'></a><a href='#L9'>9</a>
|
|
75
|
+
<a name='L10'></a><a href='#L10'>10</a>
|
|
76
|
+
<a name='L11'></a><a href='#L11'>11</a>
|
|
77
|
+
<a name='L12'></a><a href='#L12'>12</a>
|
|
78
|
+
<a name='L13'></a><a href='#L13'>13</a>
|
|
79
|
+
<a name='L14'></a><a href='#L14'>14</a>
|
|
80
|
+
<a name='L15'></a><a href='#L15'>15</a>
|
|
81
|
+
<a name='L16'></a><a href='#L16'>16</a>
|
|
82
|
+
<a name='L17'></a><a href='#L17'>17</a>
|
|
83
|
+
<a name='L18'></a><a href='#L18'>18</a>
|
|
84
|
+
<a name='L19'></a><a href='#L19'>19</a>
|
|
85
|
+
<a name='L20'></a><a href='#L20'>20</a>
|
|
86
|
+
<a name='L21'></a><a href='#L21'>21</a>
|
|
87
|
+
<a name='L22'></a><a href='#L22'>22</a>
|
|
88
|
+
<a name='L23'></a><a href='#L23'>23</a>
|
|
89
|
+
<a name='L24'></a><a href='#L24'>24</a>
|
|
90
|
+
<a name='L25'></a><a href='#L25'>25</a>
|
|
91
|
+
<a name='L26'></a><a href='#L26'>26</a>
|
|
92
|
+
<a name='L27'></a><a href='#L27'>27</a>
|
|
93
|
+
<a name='L28'></a><a href='#L28'>28</a>
|
|
94
|
+
<a name='L29'></a><a href='#L29'>29</a>
|
|
95
|
+
<a name='L30'></a><a href='#L30'>30</a>
|
|
96
|
+
<a name='L31'></a><a href='#L31'>31</a>
|
|
97
|
+
<a name='L32'></a><a href='#L32'>32</a>
|
|
98
|
+
<a name='L33'></a><a href='#L33'>33</a>
|
|
99
|
+
<a name='L34'></a><a href='#L34'>34</a>
|
|
100
|
+
<a name='L35'></a><a href='#L35'>35</a>
|
|
101
|
+
<a name='L36'></a><a href='#L36'>36</a>
|
|
102
|
+
<a name='L37'></a><a href='#L37'>37</a>
|
|
103
|
+
<a name='L38'></a><a href='#L38'>38</a>
|
|
104
|
+
<a name='L39'></a><a href='#L39'>39</a>
|
|
105
|
+
<a name='L40'></a><a href='#L40'>40</a>
|
|
106
|
+
<a name='L41'></a><a href='#L41'>41</a>
|
|
107
|
+
<a name='L42'></a><a href='#L42'>42</a>
|
|
108
|
+
<a name='L43'></a><a href='#L43'>43</a>
|
|
109
|
+
<a name='L44'></a><a href='#L44'>44</a>
|
|
110
|
+
<a name='L45'></a><a href='#L45'>45</a>
|
|
111
|
+
<a name='L46'></a><a href='#L46'>46</a>
|
|
112
|
+
<a name='L47'></a><a href='#L47'>47</a>
|
|
113
|
+
<a name='L48'></a><a href='#L48'>48</a>
|
|
114
|
+
<a name='L49'></a><a href='#L49'>49</a>
|
|
115
|
+
<a name='L50'></a><a href='#L50'>50</a>
|
|
116
|
+
<a name='L51'></a><a href='#L51'>51</a>
|
|
117
|
+
<a name='L52'></a><a href='#L52'>52</a>
|
|
118
|
+
<a name='L53'></a><a href='#L53'>53</a>
|
|
119
|
+
<a name='L54'></a><a href='#L54'>54</a>
|
|
120
|
+
<a name='L55'></a><a href='#L55'>55</a>
|
|
121
|
+
<a name='L56'></a><a href='#L56'>56</a>
|
|
122
|
+
<a name='L57'></a><a href='#L57'>57</a>
|
|
123
|
+
<a name='L58'></a><a href='#L58'>58</a>
|
|
124
|
+
<a name='L59'></a><a href='#L59'>59</a>
|
|
125
|
+
<a name='L60'></a><a href='#L60'>60</a>
|
|
126
|
+
<a name='L61'></a><a href='#L61'>61</a>
|
|
127
|
+
<a name='L62'></a><a href='#L62'>62</a>
|
|
128
|
+
<a name='L63'></a><a href='#L63'>63</a>
|
|
129
|
+
<a name='L64'></a><a href='#L64'>64</a>
|
|
130
|
+
<a name='L65'></a><a href='#L65'>65</a>
|
|
131
|
+
<a name='L66'></a><a href='#L66'>66</a>
|
|
132
|
+
<a name='L67'></a><a href='#L67'>67</a>
|
|
133
|
+
<a name='L68'></a><a href='#L68'>68</a>
|
|
134
|
+
<a name='L69'></a><a href='#L69'>69</a>
|
|
135
|
+
<a name='L70'></a><a href='#L70'>70</a>
|
|
136
|
+
<a name='L71'></a><a href='#L71'>71</a>
|
|
137
|
+
<a name='L72'></a><a href='#L72'>72</a>
|
|
138
|
+
<a name='L73'></a><a href='#L73'>73</a>
|
|
139
|
+
<a name='L74'></a><a href='#L74'>74</a>
|
|
140
|
+
<a name='L75'></a><a href='#L75'>75</a>
|
|
141
|
+
<a name='L76'></a><a href='#L76'>76</a>
|
|
142
|
+
<a name='L77'></a><a href='#L77'>77</a>
|
|
143
|
+
<a name='L78'></a><a href='#L78'>78</a>
|
|
144
|
+
<a name='L79'></a><a href='#L79'>79</a>
|
|
145
|
+
<a name='L80'></a><a href='#L80'>80</a>
|
|
146
|
+
<a name='L81'></a><a href='#L81'>81</a>
|
|
147
|
+
<a name='L82'></a><a href='#L82'>82</a>
|
|
148
|
+
<a name='L83'></a><a href='#L83'>83</a>
|
|
149
|
+
<a name='L84'></a><a href='#L84'>84</a>
|
|
150
|
+
<a name='L85'></a><a href='#L85'>85</a>
|
|
151
|
+
<a name='L86'></a><a href='#L86'>86</a>
|
|
152
|
+
<a name='L87'></a><a href='#L87'>87</a>
|
|
153
|
+
<a name='L88'></a><a href='#L88'>88</a>
|
|
154
|
+
<a name='L89'></a><a href='#L89'>89</a>
|
|
155
|
+
<a name='L90'></a><a href='#L90'>90</a>
|
|
156
|
+
<a name='L91'></a><a href='#L91'>91</a>
|
|
157
|
+
<a name='L92'></a><a href='#L92'>92</a>
|
|
158
|
+
<a name='L93'></a><a href='#L93'>93</a>
|
|
159
|
+
<a name='L94'></a><a href='#L94'>94</a>
|
|
160
|
+
<a name='L95'></a><a href='#L95'>95</a>
|
|
161
|
+
<a name='L96'></a><a href='#L96'>96</a>
|
|
162
|
+
<a name='L97'></a><a href='#L97'>97</a>
|
|
163
|
+
<a name='L98'></a><a href='#L98'>98</a>
|
|
164
|
+
<a name='L99'></a><a href='#L99'>99</a>
|
|
165
|
+
<a name='L100'></a><a href='#L100'>100</a>
|
|
166
|
+
<a name='L101'></a><a href='#L101'>101</a>
|
|
167
|
+
<a name='L102'></a><a href='#L102'>102</a>
|
|
168
|
+
<a name='L103'></a><a href='#L103'>103</a>
|
|
169
|
+
<a name='L104'></a><a href='#L104'>104</a>
|
|
170
|
+
<a name='L105'></a><a href='#L105'>105</a>
|
|
171
|
+
<a name='L106'></a><a href='#L106'>106</a>
|
|
172
|
+
<a name='L107'></a><a href='#L107'>107</a>
|
|
173
|
+
<a name='L108'></a><a href='#L108'>108</a>
|
|
174
|
+
<a name='L109'></a><a href='#L109'>109</a>
|
|
175
|
+
<a name='L110'></a><a href='#L110'>110</a>
|
|
176
|
+
<a name='L111'></a><a href='#L111'>111</a>
|
|
177
|
+
<a name='L112'></a><a href='#L112'>112</a>
|
|
178
|
+
<a name='L113'></a><a href='#L113'>113</a>
|
|
179
|
+
<a name='L114'></a><a href='#L114'>114</a>
|
|
180
|
+
<a name='L115'></a><a href='#L115'>115</a>
|
|
181
|
+
<a name='L116'></a><a href='#L116'>116</a>
|
|
182
|
+
<a name='L117'></a><a href='#L117'>117</a>
|
|
183
|
+
<a name='L118'></a><a href='#L118'>118</a>
|
|
184
|
+
<a name='L119'></a><a href='#L119'>119</a>
|
|
185
|
+
<a name='L120'></a><a href='#L120'>120</a>
|
|
186
|
+
<a name='L121'></a><a href='#L121'>121</a>
|
|
187
|
+
<a name='L122'></a><a href='#L122'>122</a>
|
|
188
|
+
<a name='L123'></a><a href='#L123'>123</a>
|
|
189
|
+
<a name='L124'></a><a href='#L124'>124</a>
|
|
190
|
+
<a name='L125'></a><a href='#L125'>125</a>
|
|
191
|
+
<a name='L126'></a><a href='#L126'>126</a>
|
|
192
|
+
<a name='L127'></a><a href='#L127'>127</a>
|
|
193
|
+
<a name='L128'></a><a href='#L128'>128</a>
|
|
194
|
+
<a name='L129'></a><a href='#L129'>129</a>
|
|
195
|
+
<a name='L130'></a><a href='#L130'>130</a>
|
|
196
|
+
<a name='L131'></a><a href='#L131'>131</a>
|
|
197
|
+
<a name='L132'></a><a href='#L132'>132</a>
|
|
198
|
+
<a name='L133'></a><a href='#L133'>133</a>
|
|
199
|
+
<a name='L134'></a><a href='#L134'>134</a>
|
|
200
|
+
<a name='L135'></a><a href='#L135'>135</a>
|
|
201
|
+
<a name='L136'></a><a href='#L136'>136</a>
|
|
202
|
+
<a name='L137'></a><a href='#L137'>137</a>
|
|
203
|
+
<a name='L138'></a><a href='#L138'>138</a>
|
|
204
|
+
<a name='L139'></a><a href='#L139'>139</a>
|
|
205
|
+
<a name='L140'></a><a href='#L140'>140</a>
|
|
206
|
+
<a name='L141'></a><a href='#L141'>141</a>
|
|
207
|
+
<a name='L142'></a><a href='#L142'>142</a>
|
|
208
|
+
<a name='L143'></a><a href='#L143'>143</a>
|
|
209
|
+
<a name='L144'></a><a href='#L144'>144</a>
|
|
210
|
+
<a name='L145'></a><a href='#L145'>145</a>
|
|
211
|
+
<a name='L146'></a><a href='#L146'>146</a>
|
|
212
|
+
<a name='L147'></a><a href='#L147'>147</a>
|
|
213
|
+
<a name='L148'></a><a href='#L148'>148</a>
|
|
214
|
+
<a name='L149'></a><a href='#L149'>149</a>
|
|
215
|
+
<a name='L150'></a><a href='#L150'>150</a>
|
|
216
|
+
<a name='L151'></a><a href='#L151'>151</a>
|
|
217
|
+
<a name='L152'></a><a href='#L152'>152</a>
|
|
218
|
+
<a name='L153'></a><a href='#L153'>153</a>
|
|
219
|
+
<a name='L154'></a><a href='#L154'>154</a>
|
|
220
|
+
<a name='L155'></a><a href='#L155'>155</a>
|
|
221
|
+
<a name='L156'></a><a href='#L156'>156</a>
|
|
222
|
+
<a name='L157'></a><a href='#L157'>157</a>
|
|
223
|
+
<a name='L158'></a><a href='#L158'>158</a>
|
|
224
|
+
<a name='L159'></a><a href='#L159'>159</a>
|
|
225
|
+
<a name='L160'></a><a href='#L160'>160</a>
|
|
226
|
+
<a name='L161'></a><a href='#L161'>161</a>
|
|
227
|
+
<a name='L162'></a><a href='#L162'>162</a>
|
|
228
|
+
<a name='L163'></a><a href='#L163'>163</a>
|
|
229
|
+
<a name='L164'></a><a href='#L164'>164</a>
|
|
230
|
+
<a name='L165'></a><a href='#L165'>165</a>
|
|
231
|
+
<a name='L166'></a><a href='#L166'>166</a>
|
|
232
|
+
<a name='L167'></a><a href='#L167'>167</a>
|
|
233
|
+
<a name='L168'></a><a href='#L168'>168</a>
|
|
234
|
+
<a name='L169'></a><a href='#L169'>169</a>
|
|
235
|
+
<a name='L170'></a><a href='#L170'>170</a>
|
|
236
|
+
<a name='L171'></a><a href='#L171'>171</a>
|
|
237
|
+
<a name='L172'></a><a href='#L172'>172</a>
|
|
238
|
+
<a name='L173'></a><a href='#L173'>173</a>
|
|
239
|
+
<a name='L174'></a><a href='#L174'>174</a>
|
|
240
|
+
<a name='L175'></a><a href='#L175'>175</a>
|
|
241
|
+
<a name='L176'></a><a href='#L176'>176</a>
|
|
242
|
+
<a name='L177'></a><a href='#L177'>177</a>
|
|
243
|
+
<a name='L178'></a><a href='#L178'>178</a>
|
|
244
|
+
<a name='L179'></a><a href='#L179'>179</a>
|
|
245
|
+
<a name='L180'></a><a href='#L180'>180</a>
|
|
246
|
+
<a name='L181'></a><a href='#L181'>181</a>
|
|
247
|
+
<a name='L182'></a><a href='#L182'>182</a>
|
|
248
|
+
<a name='L183'></a><a href='#L183'>183</a>
|
|
249
|
+
<a name='L184'></a><a href='#L184'>184</a>
|
|
250
|
+
<a name='L185'></a><a href='#L185'>185</a>
|
|
251
|
+
<a name='L186'></a><a href='#L186'>186</a>
|
|
252
|
+
<a name='L187'></a><a href='#L187'>187</a>
|
|
253
|
+
<a name='L188'></a><a href='#L188'>188</a>
|
|
254
|
+
<a name='L189'></a><a href='#L189'>189</a>
|
|
255
|
+
<a name='L190'></a><a href='#L190'>190</a>
|
|
256
|
+
<a name='L191'></a><a href='#L191'>191</a>
|
|
257
|
+
<a name='L192'></a><a href='#L192'>192</a>
|
|
258
|
+
<a name='L193'></a><a href='#L193'>193</a>
|
|
259
|
+
<a name='L194'></a><a href='#L194'>194</a>
|
|
260
|
+
<a name='L195'></a><a href='#L195'>195</a>
|
|
261
|
+
<a name='L196'></a><a href='#L196'>196</a>
|
|
262
|
+
<a name='L197'></a><a href='#L197'>197</a>
|
|
263
|
+
<a name='L198'></a><a href='#L198'>198</a>
|
|
264
|
+
<a name='L199'></a><a href='#L199'>199</a>
|
|
265
|
+
<a name='L200'></a><a href='#L200'>200</a>
|
|
266
|
+
<a name='L201'></a><a href='#L201'>201</a>
|
|
267
|
+
<a name='L202'></a><a href='#L202'>202</a>
|
|
268
|
+
<a name='L203'></a><a href='#L203'>203</a>
|
|
269
|
+
<a name='L204'></a><a href='#L204'>204</a>
|
|
270
|
+
<a name='L205'></a><a href='#L205'>205</a>
|
|
271
|
+
<a name='L206'></a><a href='#L206'>206</a>
|
|
272
|
+
<a name='L207'></a><a href='#L207'>207</a>
|
|
273
|
+
<a name='L208'></a><a href='#L208'>208</a>
|
|
274
|
+
<a name='L209'></a><a href='#L209'>209</a>
|
|
275
|
+
<a name='L210'></a><a href='#L210'>210</a>
|
|
276
|
+
<a name='L211'></a><a href='#L211'>211</a>
|
|
277
|
+
<a name='L212'></a><a href='#L212'>212</a>
|
|
278
|
+
<a name='L213'></a><a href='#L213'>213</a>
|
|
279
|
+
<a name='L214'></a><a href='#L214'>214</a>
|
|
280
|
+
<a name='L215'></a><a href='#L215'>215</a>
|
|
281
|
+
<a name='L216'></a><a href='#L216'>216</a>
|
|
282
|
+
<a name='L217'></a><a href='#L217'>217</a>
|
|
283
|
+
<a name='L218'></a><a href='#L218'>218</a>
|
|
284
|
+
<a name='L219'></a><a href='#L219'>219</a>
|
|
285
|
+
<a name='L220'></a><a href='#L220'>220</a>
|
|
286
|
+
<a name='L221'></a><a href='#L221'>221</a>
|
|
287
|
+
<a name='L222'></a><a href='#L222'>222</a>
|
|
288
|
+
<a name='L223'></a><a href='#L223'>223</a>
|
|
289
|
+
<a name='L224'></a><a href='#L224'>224</a>
|
|
290
|
+
<a name='L225'></a><a href='#L225'>225</a>
|
|
291
|
+
<a name='L226'></a><a href='#L226'>226</a>
|
|
292
|
+
<a name='L227'></a><a href='#L227'>227</a>
|
|
293
|
+
<a name='L228'></a><a href='#L228'>228</a>
|
|
294
|
+
<a name='L229'></a><a href='#L229'>229</a>
|
|
295
|
+
<a name='L230'></a><a href='#L230'>230</a>
|
|
296
|
+
<a name='L231'></a><a href='#L231'>231</a>
|
|
297
|
+
<a name='L232'></a><a href='#L232'>232</a>
|
|
298
|
+
<a name='L233'></a><a href='#L233'>233</a>
|
|
299
|
+
<a name='L234'></a><a href='#L234'>234</a>
|
|
300
|
+
<a name='L235'></a><a href='#L235'>235</a>
|
|
301
|
+
<a name='L236'></a><a href='#L236'>236</a>
|
|
302
|
+
<a name='L237'></a><a href='#L237'>237</a>
|
|
303
|
+
<a name='L238'></a><a href='#L238'>238</a>
|
|
304
|
+
<a name='L239'></a><a href='#L239'>239</a>
|
|
305
|
+
<a name='L240'></a><a href='#L240'>240</a>
|
|
306
|
+
<a name='L241'></a><a href='#L241'>241</a>
|
|
307
|
+
<a name='L242'></a><a href='#L242'>242</a>
|
|
308
|
+
<a name='L243'></a><a href='#L243'>243</a>
|
|
309
|
+
<a name='L244'></a><a href='#L244'>244</a>
|
|
310
|
+
<a name='L245'></a><a href='#L245'>245</a>
|
|
311
|
+
<a name='L246'></a><a href='#L246'>246</a>
|
|
312
|
+
<a name='L247'></a><a href='#L247'>247</a>
|
|
313
|
+
<a name='L248'></a><a href='#L248'>248</a>
|
|
314
|
+
<a name='L249'></a><a href='#L249'>249</a>
|
|
315
|
+
<a name='L250'></a><a href='#L250'>250</a>
|
|
316
|
+
<a name='L251'></a><a href='#L251'>251</a>
|
|
317
|
+
<a name='L252'></a><a href='#L252'>252</a>
|
|
318
|
+
<a name='L253'></a><a href='#L253'>253</a>
|
|
319
|
+
<a name='L254'></a><a href='#L254'>254</a>
|
|
320
|
+
<a name='L255'></a><a href='#L255'>255</a>
|
|
321
|
+
<a name='L256'></a><a href='#L256'>256</a>
|
|
322
|
+
<a name='L257'></a><a href='#L257'>257</a>
|
|
323
|
+
<a name='L258'></a><a href='#L258'>258</a>
|
|
324
|
+
<a name='L259'></a><a href='#L259'>259</a>
|
|
325
|
+
<a name='L260'></a><a href='#L260'>260</a>
|
|
326
|
+
<a name='L261'></a><a href='#L261'>261</a>
|
|
327
|
+
<a name='L262'></a><a href='#L262'>262</a>
|
|
328
|
+
<a name='L263'></a><a href='#L263'>263</a>
|
|
329
|
+
<a name='L264'></a><a href='#L264'>264</a>
|
|
330
|
+
<a name='L265'></a><a href='#L265'>265</a>
|
|
331
|
+
<a name='L266'></a><a href='#L266'>266</a>
|
|
332
|
+
<a name='L267'></a><a href='#L267'>267</a>
|
|
333
|
+
<a name='L268'></a><a href='#L268'>268</a>
|
|
334
|
+
<a name='L269'></a><a href='#L269'>269</a>
|
|
335
|
+
<a name='L270'></a><a href='#L270'>270</a>
|
|
336
|
+
<a name='L271'></a><a href='#L271'>271</a>
|
|
337
|
+
<a name='L272'></a><a href='#L272'>272</a>
|
|
338
|
+
<a name='L273'></a><a href='#L273'>273</a>
|
|
339
|
+
<a name='L274'></a><a href='#L274'>274</a>
|
|
340
|
+
<a name='L275'></a><a href='#L275'>275</a>
|
|
341
|
+
<a name='L276'></a><a href='#L276'>276</a>
|
|
342
|
+
<a name='L277'></a><a href='#L277'>277</a>
|
|
343
|
+
<a name='L278'></a><a href='#L278'>278</a>
|
|
344
|
+
<a name='L279'></a><a href='#L279'>279</a>
|
|
345
|
+
<a name='L280'></a><a href='#L280'>280</a>
|
|
346
|
+
<a name='L281'></a><a href='#L281'>281</a>
|
|
347
|
+
<a name='L282'></a><a href='#L282'>282</a>
|
|
348
|
+
<a name='L283'></a><a href='#L283'>283</a>
|
|
349
|
+
<a name='L284'></a><a href='#L284'>284</a>
|
|
350
|
+
<a name='L285'></a><a href='#L285'>285</a>
|
|
351
|
+
<a name='L286'></a><a href='#L286'>286</a>
|
|
352
|
+
<a name='L287'></a><a href='#L287'>287</a>
|
|
353
|
+
<a name='L288'></a><a href='#L288'>288</a>
|
|
354
|
+
<a name='L289'></a><a href='#L289'>289</a>
|
|
355
|
+
<a name='L290'></a><a href='#L290'>290</a>
|
|
356
|
+
<a name='L291'></a><a href='#L291'>291</a>
|
|
357
|
+
<a name='L292'></a><a href='#L292'>292</a>
|
|
358
|
+
<a name='L293'></a><a href='#L293'>293</a>
|
|
359
|
+
<a name='L294'></a><a href='#L294'>294</a>
|
|
360
|
+
<a name='L295'></a><a href='#L295'>295</a>
|
|
361
|
+
<a name='L296'></a><a href='#L296'>296</a>
|
|
362
|
+
<a name='L297'></a><a href='#L297'>297</a>
|
|
363
|
+
<a name='L298'></a><a href='#L298'>298</a>
|
|
364
|
+
<a name='L299'></a><a href='#L299'>299</a>
|
|
365
|
+
<a name='L300'></a><a href='#L300'>300</a>
|
|
366
|
+
<a name='L301'></a><a href='#L301'>301</a>
|
|
367
|
+
<a name='L302'></a><a href='#L302'>302</a>
|
|
368
|
+
<a name='L303'></a><a href='#L303'>303</a>
|
|
369
|
+
<a name='L304'></a><a href='#L304'>304</a>
|
|
370
|
+
<a name='L305'></a><a href='#L305'>305</a>
|
|
371
|
+
<a name='L306'></a><a href='#L306'>306</a>
|
|
372
|
+
<a name='L307'></a><a href='#L307'>307</a>
|
|
373
|
+
<a name='L308'></a><a href='#L308'>308</a>
|
|
374
|
+
<a name='L309'></a><a href='#L309'>309</a>
|
|
375
|
+
<a name='L310'></a><a href='#L310'>310</a>
|
|
376
|
+
<a name='L311'></a><a href='#L311'>311</a>
|
|
377
|
+
<a name='L312'></a><a href='#L312'>312</a>
|
|
378
|
+
<a name='L313'></a><a href='#L313'>313</a>
|
|
379
|
+
<a name='L314'></a><a href='#L314'>314</a>
|
|
380
|
+
<a name='L315'></a><a href='#L315'>315</a>
|
|
381
|
+
<a name='L316'></a><a href='#L316'>316</a>
|
|
382
|
+
<a name='L317'></a><a href='#L317'>317</a>
|
|
383
|
+
<a name='L318'></a><a href='#L318'>318</a>
|
|
384
|
+
<a name='L319'></a><a href='#L319'>319</a>
|
|
385
|
+
<a name='L320'></a><a href='#L320'>320</a>
|
|
386
|
+
<a name='L321'></a><a href='#L321'>321</a>
|
|
387
|
+
<a name='L322'></a><a href='#L322'>322</a>
|
|
388
|
+
<a name='L323'></a><a href='#L323'>323</a>
|
|
389
|
+
<a name='L324'></a><a href='#L324'>324</a>
|
|
390
|
+
<a name='L325'></a><a href='#L325'>325</a>
|
|
391
|
+
<a name='L326'></a><a href='#L326'>326</a>
|
|
392
|
+
<a name='L327'></a><a href='#L327'>327</a>
|
|
393
|
+
<a name='L328'></a><a href='#L328'>328</a>
|
|
394
|
+
<a name='L329'></a><a href='#L329'>329</a>
|
|
395
|
+
<a name='L330'></a><a href='#L330'>330</a>
|
|
396
|
+
<a name='L331'></a><a href='#L331'>331</a>
|
|
397
|
+
<a name='L332'></a><a href='#L332'>332</a>
|
|
398
|
+
<a name='L333'></a><a href='#L333'>333</a>
|
|
399
|
+
<a name='L334'></a><a href='#L334'>334</a>
|
|
400
|
+
<a name='L335'></a><a href='#L335'>335</a>
|
|
401
|
+
<a name='L336'></a><a href='#L336'>336</a>
|
|
402
|
+
<a name='L337'></a><a href='#L337'>337</a>
|
|
403
|
+
<a name='L338'></a><a href='#L338'>338</a>
|
|
404
|
+
<a name='L339'></a><a href='#L339'>339</a>
|
|
405
|
+
<a name='L340'></a><a href='#L340'>340</a>
|
|
406
|
+
<a name='L341'></a><a href='#L341'>341</a>
|
|
407
|
+
<a name='L342'></a><a href='#L342'>342</a>
|
|
408
|
+
<a name='L343'></a><a href='#L343'>343</a>
|
|
409
|
+
<a name='L344'></a><a href='#L344'>344</a>
|
|
410
|
+
<a name='L345'></a><a href='#L345'>345</a>
|
|
411
|
+
<a name='L346'></a><a href='#L346'>346</a>
|
|
412
|
+
<a name='L347'></a><a href='#L347'>347</a>
|
|
413
|
+
<a name='L348'></a><a href='#L348'>348</a>
|
|
414
|
+
<a name='L349'></a><a href='#L349'>349</a>
|
|
415
|
+
<a name='L350'></a><a href='#L350'>350</a>
|
|
416
|
+
<a name='L351'></a><a href='#L351'>351</a>
|
|
417
|
+
<a name='L352'></a><a href='#L352'>352</a>
|
|
418
|
+
<a name='L353'></a><a href='#L353'>353</a>
|
|
419
|
+
<a name='L354'></a><a href='#L354'>354</a>
|
|
420
|
+
<a name='L355'></a><a href='#L355'>355</a>
|
|
421
|
+
<a name='L356'></a><a href='#L356'>356</a>
|
|
422
|
+
<a name='L357'></a><a href='#L357'>357</a>
|
|
423
|
+
<a name='L358'></a><a href='#L358'>358</a>
|
|
424
|
+
<a name='L359'></a><a href='#L359'>359</a>
|
|
425
|
+
<a name='L360'></a><a href='#L360'>360</a>
|
|
426
|
+
<a name='L361'></a><a href='#L361'>361</a>
|
|
427
|
+
<a name='L362'></a><a href='#L362'>362</a>
|
|
428
|
+
<a name='L363'></a><a href='#L363'>363</a>
|
|
429
|
+
<a name='L364'></a><a href='#L364'>364</a>
|
|
430
|
+
<a name='L365'></a><a href='#L365'>365</a>
|
|
431
|
+
<a name='L366'></a><a href='#L366'>366</a>
|
|
432
|
+
<a name='L367'></a><a href='#L367'>367</a>
|
|
433
|
+
<a name='L368'></a><a href='#L368'>368</a>
|
|
434
|
+
<a name='L369'></a><a href='#L369'>369</a>
|
|
435
|
+
<a name='L370'></a><a href='#L370'>370</a>
|
|
436
|
+
<a name='L371'></a><a href='#L371'>371</a>
|
|
437
|
+
<a name='L372'></a><a href='#L372'>372</a>
|
|
438
|
+
<a name='L373'></a><a href='#L373'>373</a>
|
|
439
|
+
<a name='L374'></a><a href='#L374'>374</a>
|
|
440
|
+
<a name='L375'></a><a href='#L375'>375</a>
|
|
441
|
+
<a name='L376'></a><a href='#L376'>376</a>
|
|
442
|
+
<a name='L377'></a><a href='#L377'>377</a>
|
|
443
|
+
<a name='L378'></a><a href='#L378'>378</a>
|
|
444
|
+
<a name='L379'></a><a href='#L379'>379</a>
|
|
445
|
+
<a name='L380'></a><a href='#L380'>380</a>
|
|
446
|
+
<a name='L381'></a><a href='#L381'>381</a>
|
|
447
|
+
<a name='L382'></a><a href='#L382'>382</a>
|
|
448
|
+
<a name='L383'></a><a href='#L383'>383</a>
|
|
449
|
+
<a name='L384'></a><a href='#L384'>384</a>
|
|
450
|
+
<a name='L385'></a><a href='#L385'>385</a>
|
|
451
|
+
<a name='L386'></a><a href='#L386'>386</a>
|
|
452
|
+
<a name='L387'></a><a href='#L387'>387</a>
|
|
453
|
+
<a name='L388'></a><a href='#L388'>388</a>
|
|
454
|
+
<a name='L389'></a><a href='#L389'>389</a>
|
|
455
|
+
<a name='L390'></a><a href='#L390'>390</a>
|
|
456
|
+
<a name='L391'></a><a href='#L391'>391</a>
|
|
457
|
+
<a name='L392'></a><a href='#L392'>392</a>
|
|
458
|
+
<a name='L393'></a><a href='#L393'>393</a>
|
|
459
|
+
<a name='L394'></a><a href='#L394'>394</a>
|
|
460
|
+
<a name='L395'></a><a href='#L395'>395</a>
|
|
461
|
+
<a name='L396'></a><a href='#L396'>396</a>
|
|
462
|
+
<a name='L397'></a><a href='#L397'>397</a>
|
|
463
|
+
<a name='L398'></a><a href='#L398'>398</a>
|
|
464
|
+
<a name='L399'></a><a href='#L399'>399</a>
|
|
465
|
+
<a name='L400'></a><a href='#L400'>400</a>
|
|
466
|
+
<a name='L401'></a><a href='#L401'>401</a>
|
|
467
|
+
<a name='L402'></a><a href='#L402'>402</a>
|
|
468
|
+
<a name='L403'></a><a href='#L403'>403</a>
|
|
469
|
+
<a name='L404'></a><a href='#L404'>404</a>
|
|
470
|
+
<a name='L405'></a><a href='#L405'>405</a>
|
|
471
|
+
<a name='L406'></a><a href='#L406'>406</a>
|
|
472
|
+
<a name='L407'></a><a href='#L407'>407</a>
|
|
473
|
+
<a name='L408'></a><a href='#L408'>408</a>
|
|
474
|
+
<a name='L409'></a><a href='#L409'>409</a>
|
|
475
|
+
<a name='L410'></a><a href='#L410'>410</a>
|
|
476
|
+
<a name='L411'></a><a href='#L411'>411</a>
|
|
477
|
+
<a name='L412'></a><a href='#L412'>412</a>
|
|
478
|
+
<a name='L413'></a><a href='#L413'>413</a>
|
|
479
|
+
<a name='L414'></a><a href='#L414'>414</a>
|
|
480
|
+
<a name='L415'></a><a href='#L415'>415</a>
|
|
481
|
+
<a name='L416'></a><a href='#L416'>416</a>
|
|
482
|
+
<a name='L417'></a><a href='#L417'>417</a>
|
|
483
|
+
<a name='L418'></a><a href='#L418'>418</a>
|
|
484
|
+
<a name='L419'></a><a href='#L419'>419</a>
|
|
485
|
+
<a name='L420'></a><a href='#L420'>420</a>
|
|
486
|
+
<a name='L421'></a><a href='#L421'>421</a>
|
|
487
|
+
<a name='L422'></a><a href='#L422'>422</a>
|
|
488
|
+
<a name='L423'></a><a href='#L423'>423</a>
|
|
489
|
+
<a name='L424'></a><a href='#L424'>424</a>
|
|
490
|
+
<a name='L425'></a><a href='#L425'>425</a>
|
|
491
|
+
<a name='L426'></a><a href='#L426'>426</a>
|
|
492
|
+
<a name='L427'></a><a href='#L427'>427</a>
|
|
493
|
+
<a name='L428'></a><a href='#L428'>428</a>
|
|
494
|
+
<a name='L429'></a><a href='#L429'>429</a>
|
|
495
|
+
<a name='L430'></a><a href='#L430'>430</a>
|
|
496
|
+
<a name='L431'></a><a href='#L431'>431</a>
|
|
497
|
+
<a name='L432'></a><a href='#L432'>432</a>
|
|
498
|
+
<a name='L433'></a><a href='#L433'>433</a>
|
|
499
|
+
<a name='L434'></a><a href='#L434'>434</a>
|
|
500
|
+
<a name='L435'></a><a href='#L435'>435</a>
|
|
501
|
+
<a name='L436'></a><a href='#L436'>436</a>
|
|
502
|
+
<a name='L437'></a><a href='#L437'>437</a>
|
|
503
|
+
<a name='L438'></a><a href='#L438'>438</a>
|
|
504
|
+
<a name='L439'></a><a href='#L439'>439</a>
|
|
505
|
+
<a name='L440'></a><a href='#L440'>440</a>
|
|
506
|
+
<a name='L441'></a><a href='#L441'>441</a>
|
|
507
|
+
<a name='L442'></a><a href='#L442'>442</a>
|
|
508
|
+
<a name='L443'></a><a href='#L443'>443</a>
|
|
509
|
+
<a name='L444'></a><a href='#L444'>444</a>
|
|
510
|
+
<a name='L445'></a><a href='#L445'>445</a>
|
|
511
|
+
<a name='L446'></a><a href='#L446'>446</a>
|
|
512
|
+
<a name='L447'></a><a href='#L447'>447</a>
|
|
513
|
+
<a name='L448'></a><a href='#L448'>448</a>
|
|
514
|
+
<a name='L449'></a><a href='#L449'>449</a>
|
|
515
|
+
<a name='L450'></a><a href='#L450'>450</a>
|
|
516
|
+
<a name='L451'></a><a href='#L451'>451</a>
|
|
517
|
+
<a name='L452'></a><a href='#L452'>452</a>
|
|
518
|
+
<a name='L453'></a><a href='#L453'>453</a>
|
|
519
|
+
<a name='L454'></a><a href='#L454'>454</a>
|
|
520
|
+
<a name='L455'></a><a href='#L455'>455</a>
|
|
521
|
+
<a name='L456'></a><a href='#L456'>456</a>
|
|
522
|
+
<a name='L457'></a><a href='#L457'>457</a>
|
|
523
|
+
<a name='L458'></a><a href='#L458'>458</a>
|
|
524
|
+
<a name='L459'></a><a href='#L459'>459</a>
|
|
525
|
+
<a name='L460'></a><a href='#L460'>460</a>
|
|
526
|
+
<a name='L461'></a><a href='#L461'>461</a>
|
|
527
|
+
<a name='L462'></a><a href='#L462'>462</a>
|
|
528
|
+
<a name='L463'></a><a href='#L463'>463</a>
|
|
529
|
+
<a name='L464'></a><a href='#L464'>464</a>
|
|
530
|
+
<a name='L465'></a><a href='#L465'>465</a>
|
|
531
|
+
<a name='L466'></a><a href='#L466'>466</a>
|
|
532
|
+
<a name='L467'></a><a href='#L467'>467</a>
|
|
533
|
+
<a name='L468'></a><a href='#L468'>468</a>
|
|
534
|
+
<a name='L469'></a><a href='#L469'>469</a>
|
|
535
|
+
<a name='L470'></a><a href='#L470'>470</a>
|
|
536
|
+
<a name='L471'></a><a href='#L471'>471</a>
|
|
537
|
+
<a name='L472'></a><a href='#L472'>472</a>
|
|
538
|
+
<a name='L473'></a><a href='#L473'>473</a>
|
|
539
|
+
<a name='L474'></a><a href='#L474'>474</a>
|
|
540
|
+
<a name='L475'></a><a href='#L475'>475</a>
|
|
541
|
+
<a name='L476'></a><a href='#L476'>476</a>
|
|
542
|
+
<a name='L477'></a><a href='#L477'>477</a>
|
|
543
|
+
<a name='L478'></a><a href='#L478'>478</a>
|
|
544
|
+
<a name='L479'></a><a href='#L479'>479</a>
|
|
545
|
+
<a name='L480'></a><a href='#L480'>480</a>
|
|
546
|
+
<a name='L481'></a><a href='#L481'>481</a>
|
|
547
|
+
<a name='L482'></a><a href='#L482'>482</a>
|
|
548
|
+
<a name='L483'></a><a href='#L483'>483</a>
|
|
549
|
+
<a name='L484'></a><a href='#L484'>484</a>
|
|
550
|
+
<a name='L485'></a><a href='#L485'>485</a>
|
|
551
|
+
<a name='L486'></a><a href='#L486'>486</a>
|
|
552
|
+
<a name='L487'></a><a href='#L487'>487</a>
|
|
553
|
+
<a name='L488'></a><a href='#L488'>488</a>
|
|
554
|
+
<a name='L489'></a><a href='#L489'>489</a>
|
|
555
|
+
<a name='L490'></a><a href='#L490'>490</a>
|
|
556
|
+
<a name='L491'></a><a href='#L491'>491</a>
|
|
557
|
+
<a name='L492'></a><a href='#L492'>492</a>
|
|
558
|
+
<a name='L493'></a><a href='#L493'>493</a>
|
|
559
|
+
<a name='L494'></a><a href='#L494'>494</a>
|
|
560
|
+
<a name='L495'></a><a href='#L495'>495</a>
|
|
561
|
+
<a name='L496'></a><a href='#L496'>496</a>
|
|
562
|
+
<a name='L497'></a><a href='#L497'>497</a>
|
|
563
|
+
<a name='L498'></a><a href='#L498'>498</a>
|
|
564
|
+
<a name='L499'></a><a href='#L499'>499</a>
|
|
565
|
+
<a name='L500'></a><a href='#L500'>500</a>
|
|
566
|
+
<a name='L501'></a><a href='#L501'>501</a>
|
|
567
|
+
<a name='L502'></a><a href='#L502'>502</a>
|
|
568
|
+
<a name='L503'></a><a href='#L503'>503</a>
|
|
569
|
+
<a name='L504'></a><a href='#L504'>504</a>
|
|
570
|
+
<a name='L505'></a><a href='#L505'>505</a>
|
|
571
|
+
<a name='L506'></a><a href='#L506'>506</a>
|
|
572
|
+
<a name='L507'></a><a href='#L507'>507</a>
|
|
573
|
+
<a name='L508'></a><a href='#L508'>508</a>
|
|
574
|
+
<a name='L509'></a><a href='#L509'>509</a>
|
|
575
|
+
<a name='L510'></a><a href='#L510'>510</a>
|
|
576
|
+
<a name='L511'></a><a href='#L511'>511</a>
|
|
577
|
+
<a name='L512'></a><a href='#L512'>512</a>
|
|
578
|
+
<a name='L513'></a><a href='#L513'>513</a>
|
|
579
|
+
<a name='L514'></a><a href='#L514'>514</a>
|
|
580
|
+
<a name='L515'></a><a href='#L515'>515</a>
|
|
581
|
+
<a name='L516'></a><a href='#L516'>516</a>
|
|
582
|
+
<a name='L517'></a><a href='#L517'>517</a>
|
|
583
|
+
<a name='L518'></a><a href='#L518'>518</a>
|
|
584
|
+
<a name='L519'></a><a href='#L519'>519</a>
|
|
585
|
+
<a name='L520'></a><a href='#L520'>520</a>
|
|
586
|
+
<a name='L521'></a><a href='#L521'>521</a>
|
|
587
|
+
<a name='L522'></a><a href='#L522'>522</a>
|
|
588
|
+
<a name='L523'></a><a href='#L523'>523</a>
|
|
589
|
+
<a name='L524'></a><a href='#L524'>524</a>
|
|
590
|
+
<a name='L525'></a><a href='#L525'>525</a>
|
|
591
|
+
<a name='L526'></a><a href='#L526'>526</a>
|
|
592
|
+
<a name='L527'></a><a href='#L527'>527</a>
|
|
593
|
+
<a name='L528'></a><a href='#L528'>528</a>
|
|
594
|
+
<a name='L529'></a><a href='#L529'>529</a>
|
|
595
|
+
<a name='L530'></a><a href='#L530'>530</a>
|
|
596
|
+
<a name='L531'></a><a href='#L531'>531</a>
|
|
597
|
+
<a name='L532'></a><a href='#L532'>532</a>
|
|
598
|
+
<a name='L533'></a><a href='#L533'>533</a>
|
|
599
|
+
<a name='L534'></a><a href='#L534'>534</a>
|
|
600
|
+
<a name='L535'></a><a href='#L535'>535</a>
|
|
601
|
+
<a name='L536'></a><a href='#L536'>536</a>
|
|
602
|
+
<a name='L537'></a><a href='#L537'>537</a>
|
|
603
|
+
<a name='L538'></a><a href='#L538'>538</a>
|
|
604
|
+
<a name='L539'></a><a href='#L539'>539</a>
|
|
605
|
+
<a name='L540'></a><a href='#L540'>540</a>
|
|
606
|
+
<a name='L541'></a><a href='#L541'>541</a>
|
|
607
|
+
<a name='L542'></a><a href='#L542'>542</a>
|
|
608
|
+
<a name='L543'></a><a href='#L543'>543</a>
|
|
609
|
+
<a name='L544'></a><a href='#L544'>544</a>
|
|
610
|
+
<a name='L545'></a><a href='#L545'>545</a>
|
|
611
|
+
<a name='L546'></a><a href='#L546'>546</a>
|
|
612
|
+
<a name='L547'></a><a href='#L547'>547</a>
|
|
613
|
+
<a name='L548'></a><a href='#L548'>548</a>
|
|
614
|
+
<a name='L549'></a><a href='#L549'>549</a>
|
|
615
|
+
<a name='L550'></a><a href='#L550'>550</a>
|
|
616
|
+
<a name='L551'></a><a href='#L551'>551</a>
|
|
617
|
+
<a name='L552'></a><a href='#L552'>552</a>
|
|
618
|
+
<a name='L553'></a><a href='#L553'>553</a>
|
|
619
|
+
<a name='L554'></a><a href='#L554'>554</a>
|
|
620
|
+
<a name='L555'></a><a href='#L555'>555</a>
|
|
621
|
+
<a name='L556'></a><a href='#L556'>556</a>
|
|
622
|
+
<a name='L557'></a><a href='#L557'>557</a>
|
|
623
|
+
<a name='L558'></a><a href='#L558'>558</a>
|
|
624
|
+
<a name='L559'></a><a href='#L559'>559</a>
|
|
625
|
+
<a name='L560'></a><a href='#L560'>560</a>
|
|
626
|
+
<a name='L561'></a><a href='#L561'>561</a>
|
|
627
|
+
<a name='L562'></a><a href='#L562'>562</a>
|
|
628
|
+
<a name='L563'></a><a href='#L563'>563</a>
|
|
629
|
+
<a name='L564'></a><a href='#L564'>564</a>
|
|
630
|
+
<a name='L565'></a><a href='#L565'>565</a>
|
|
631
|
+
<a name='L566'></a><a href='#L566'>566</a>
|
|
632
|
+
<a name='L567'></a><a href='#L567'>567</a>
|
|
633
|
+
<a name='L568'></a><a href='#L568'>568</a>
|
|
634
|
+
<a name='L569'></a><a href='#L569'>569</a>
|
|
635
|
+
<a name='L570'></a><a href='#L570'>570</a>
|
|
636
|
+
<a name='L571'></a><a href='#L571'>571</a>
|
|
637
|
+
<a name='L572'></a><a href='#L572'>572</a>
|
|
638
|
+
<a name='L573'></a><a href='#L573'>573</a>
|
|
639
|
+
<a name='L574'></a><a href='#L574'>574</a>
|
|
640
|
+
<a name='L575'></a><a href='#L575'>575</a>
|
|
641
|
+
<a name='L576'></a><a href='#L576'>576</a>
|
|
642
|
+
<a name='L577'></a><a href='#L577'>577</a>
|
|
643
|
+
<a name='L578'></a><a href='#L578'>578</a>
|
|
644
|
+
<a name='L579'></a><a href='#L579'>579</a>
|
|
645
|
+
<a name='L580'></a><a href='#L580'>580</a>
|
|
646
|
+
<a name='L581'></a><a href='#L581'>581</a>
|
|
647
|
+
<a name='L582'></a><a href='#L582'>582</a>
|
|
648
|
+
<a name='L583'></a><a href='#L583'>583</a>
|
|
649
|
+
<a name='L584'></a><a href='#L584'>584</a>
|
|
650
|
+
<a name='L585'></a><a href='#L585'>585</a>
|
|
651
|
+
<a name='L586'></a><a href='#L586'>586</a>
|
|
652
|
+
<a name='L587'></a><a href='#L587'>587</a>
|
|
653
|
+
<a name='L588'></a><a href='#L588'>588</a>
|
|
654
|
+
<a name='L589'></a><a href='#L589'>589</a>
|
|
655
|
+
<a name='L590'></a><a href='#L590'>590</a>
|
|
656
|
+
<a name='L591'></a><a href='#L591'>591</a>
|
|
657
|
+
<a name='L592'></a><a href='#L592'>592</a>
|
|
658
|
+
<a name='L593'></a><a href='#L593'>593</a>
|
|
659
|
+
<a name='L594'></a><a href='#L594'>594</a>
|
|
660
|
+
<a name='L595'></a><a href='#L595'>595</a>
|
|
661
|
+
<a name='L596'></a><a href='#L596'>596</a>
|
|
662
|
+
<a name='L597'></a><a href='#L597'>597</a>
|
|
663
|
+
<a name='L598'></a><a href='#L598'>598</a>
|
|
664
|
+
<a name='L599'></a><a href='#L599'>599</a>
|
|
665
|
+
<a name='L600'></a><a href='#L600'>600</a>
|
|
666
|
+
<a name='L601'></a><a href='#L601'>601</a>
|
|
667
|
+
<a name='L602'></a><a href='#L602'>602</a>
|
|
668
|
+
<a name='L603'></a><a href='#L603'>603</a>
|
|
669
|
+
<a name='L604'></a><a href='#L604'>604</a>
|
|
670
|
+
<a name='L605'></a><a href='#L605'>605</a>
|
|
671
|
+
<a name='L606'></a><a href='#L606'>606</a>
|
|
672
|
+
<a name='L607'></a><a href='#L607'>607</a>
|
|
673
|
+
<a name='L608'></a><a href='#L608'>608</a>
|
|
674
|
+
<a name='L609'></a><a href='#L609'>609</a>
|
|
675
|
+
<a name='L610'></a><a href='#L610'>610</a>
|
|
676
|
+
<a name='L611'></a><a href='#L611'>611</a>
|
|
677
|
+
<a name='L612'></a><a href='#L612'>612</a>
|
|
678
|
+
<a name='L613'></a><a href='#L613'>613</a>
|
|
679
|
+
<a name='L614'></a><a href='#L614'>614</a>
|
|
680
|
+
<a name='L615'></a><a href='#L615'>615</a>
|
|
681
|
+
<a name='L616'></a><a href='#L616'>616</a>
|
|
682
|
+
<a name='L617'></a><a href='#L617'>617</a>
|
|
683
|
+
<a name='L618'></a><a href='#L618'>618</a>
|
|
684
|
+
<a name='L619'></a><a href='#L619'>619</a>
|
|
685
|
+
<a name='L620'></a><a href='#L620'>620</a>
|
|
686
|
+
<a name='L621'></a><a href='#L621'>621</a>
|
|
687
|
+
<a name='L622'></a><a href='#L622'>622</a>
|
|
688
|
+
<a name='L623'></a><a href='#L623'>623</a>
|
|
689
|
+
<a name='L624'></a><a href='#L624'>624</a>
|
|
690
|
+
<a name='L625'></a><a href='#L625'>625</a>
|
|
691
|
+
<a name='L626'></a><a href='#L626'>626</a>
|
|
692
|
+
<a name='L627'></a><a href='#L627'>627</a>
|
|
693
|
+
<a name='L628'></a><a href='#L628'>628</a>
|
|
694
|
+
<a name='L629'></a><a href='#L629'>629</a>
|
|
695
|
+
<a name='L630'></a><a href='#L630'>630</a>
|
|
696
|
+
<a name='L631'></a><a href='#L631'>631</a>
|
|
697
|
+
<a name='L632'></a><a href='#L632'>632</a>
|
|
698
|
+
<a name='L633'></a><a href='#L633'>633</a>
|
|
699
|
+
<a name='L634'></a><a href='#L634'>634</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
|
|
700
|
+
<span class="cline-any cline-neutral"> </span>
|
|
701
|
+
<span class="cline-any cline-neutral"> </span>
|
|
702
|
+
<span class="cline-any cline-neutral"> </span>
|
|
703
|
+
<span class="cline-any cline-neutral"> </span>
|
|
704
|
+
<span class="cline-any cline-neutral"> </span>
|
|
705
|
+
<span class="cline-any cline-neutral"> </span>
|
|
706
|
+
<span class="cline-any cline-neutral"> </span>
|
|
707
|
+
<span class="cline-any cline-neutral"> </span>
|
|
708
|
+
<span class="cline-any cline-neutral"> </span>
|
|
709
|
+
<span class="cline-any cline-neutral"> </span>
|
|
710
|
+
<span class="cline-any cline-neutral"> </span>
|
|
711
|
+
<span class="cline-any cline-neutral"> </span>
|
|
712
|
+
<span class="cline-any cline-neutral"> </span>
|
|
713
|
+
<span class="cline-any cline-neutral"> </span>
|
|
714
|
+
<span class="cline-any cline-neutral"> </span>
|
|
715
|
+
<span class="cline-any cline-neutral"> </span>
|
|
716
|
+
<span class="cline-any cline-neutral"> </span>
|
|
717
|
+
<span class="cline-any cline-neutral"> </span>
|
|
718
|
+
<span class="cline-any cline-neutral"> </span>
|
|
719
|
+
<span class="cline-any cline-neutral"> </span>
|
|
720
|
+
<span class="cline-any cline-neutral"> </span>
|
|
721
|
+
<span class="cline-any cline-neutral"> </span>
|
|
722
|
+
<span class="cline-any cline-neutral"> </span>
|
|
723
|
+
<span class="cline-any cline-neutral"> </span>
|
|
724
|
+
<span class="cline-any cline-neutral"> </span>
|
|
725
|
+
<span class="cline-any cline-neutral"> </span>
|
|
726
|
+
<span class="cline-any cline-neutral"> </span>
|
|
727
|
+
<span class="cline-any cline-neutral"> </span>
|
|
728
|
+
<span class="cline-any cline-neutral"> </span>
|
|
729
|
+
<span class="cline-any cline-neutral"> </span>
|
|
730
|
+
<span class="cline-any cline-yes">1x</span>
|
|
731
|
+
<span class="cline-any cline-neutral"> </span>
|
|
732
|
+
<span class="cline-any cline-neutral"> </span>
|
|
733
|
+
<span class="cline-any cline-neutral"> </span>
|
|
734
|
+
<span class="cline-any cline-neutral"> </span>
|
|
735
|
+
<span class="cline-any cline-neutral"> </span>
|
|
736
|
+
<span class="cline-any cline-neutral"> </span>
|
|
737
|
+
<span class="cline-any cline-neutral"> </span>
|
|
738
|
+
<span class="cline-any cline-neutral"> </span>
|
|
739
|
+
<span class="cline-any cline-neutral"> </span>
|
|
740
|
+
<span class="cline-any cline-neutral"> </span>
|
|
741
|
+
<span class="cline-any cline-neutral"> </span>
|
|
742
|
+
<span class="cline-any cline-neutral"> </span>
|
|
743
|
+
<span class="cline-any cline-neutral"> </span>
|
|
744
|
+
<span class="cline-any cline-neutral"> </span>
|
|
745
|
+
<span class="cline-any cline-neutral"> </span>
|
|
746
|
+
<span class="cline-any cline-neutral"> </span>
|
|
747
|
+
<span class="cline-any cline-neutral"> </span>
|
|
748
|
+
<span class="cline-any cline-neutral"> </span>
|
|
749
|
+
<span class="cline-any cline-neutral"> </span>
|
|
750
|
+
<span class="cline-any cline-neutral"> </span>
|
|
751
|
+
<span class="cline-any cline-neutral"> </span>
|
|
752
|
+
<span class="cline-any cline-neutral"> </span>
|
|
753
|
+
<span class="cline-any cline-neutral"> </span>
|
|
754
|
+
<span class="cline-any cline-neutral"> </span>
|
|
755
|
+
<span class="cline-any cline-neutral"> </span>
|
|
756
|
+
<span class="cline-any cline-neutral"> </span>
|
|
757
|
+
<span class="cline-any cline-neutral"> </span>
|
|
758
|
+
<span class="cline-any cline-neutral"> </span>
|
|
759
|
+
<span class="cline-any cline-neutral"> </span>
|
|
760
|
+
<span class="cline-any cline-neutral"> </span>
|
|
761
|
+
<span class="cline-any cline-neutral"> </span>
|
|
762
|
+
<span class="cline-any cline-neutral"> </span>
|
|
763
|
+
<span class="cline-any cline-neutral"> </span>
|
|
764
|
+
<span class="cline-any cline-neutral"> </span>
|
|
765
|
+
<span class="cline-any cline-neutral"> </span>
|
|
766
|
+
<span class="cline-any cline-neutral"> </span>
|
|
767
|
+
<span class="cline-any cline-neutral"> </span>
|
|
768
|
+
<span class="cline-any cline-neutral"> </span>
|
|
769
|
+
<span class="cline-any cline-neutral"> </span>
|
|
770
|
+
<span class="cline-any cline-neutral"> </span>
|
|
771
|
+
<span class="cline-any cline-neutral"> </span>
|
|
772
|
+
<span class="cline-any cline-neutral"> </span>
|
|
773
|
+
<span class="cline-any cline-neutral"> </span>
|
|
774
|
+
<span class="cline-any cline-neutral"> </span>
|
|
775
|
+
<span class="cline-any cline-yes">1x</span>
|
|
776
|
+
<span class="cline-any cline-yes">13x</span>
|
|
777
|
+
<span class="cline-any cline-yes">13x</span>
|
|
778
|
+
<span class="cline-any cline-yes">13x</span>
|
|
779
|
+
<span class="cline-any cline-neutral"> </span>
|
|
780
|
+
<span class="cline-any cline-yes">13x</span>
|
|
781
|
+
<span class="cline-any cline-yes">13x</span>
|
|
782
|
+
<span class="cline-any cline-neutral"> </span>
|
|
783
|
+
<span class="cline-any cline-yes">13x</span>
|
|
784
|
+
<span class="cline-any cline-yes">13x</span>
|
|
785
|
+
<span class="cline-any cline-yes">13x</span>
|
|
786
|
+
<span class="cline-any cline-yes">13x</span>
|
|
787
|
+
<span class="cline-any cline-neutral"> </span>
|
|
788
|
+
<span class="cline-any cline-yes">13x</span>
|
|
789
|
+
<span class="cline-any cline-yes">11x</span>
|
|
790
|
+
<span class="cline-any cline-yes">11x</span>
|
|
791
|
+
<span class="cline-any cline-yes">11x</span>
|
|
792
|
+
<span class="cline-any cline-yes">11x</span>
|
|
793
|
+
<span class="cline-any cline-yes">11x</span>
|
|
794
|
+
<span class="cline-any cline-yes">11x</span>
|
|
795
|
+
<span class="cline-any cline-yes">11x</span>
|
|
796
|
+
<span class="cline-any cline-yes">11x</span>
|
|
797
|
+
<span class="cline-any cline-neutral"> </span>
|
|
798
|
+
<span class="cline-any cline-yes">13x</span>
|
|
799
|
+
<span class="cline-any cline-yes">21x</span>
|
|
800
|
+
<span class="cline-any cline-yes">21x</span>
|
|
801
|
+
<span class="cline-any cline-yes">11x</span>
|
|
802
|
+
<span class="cline-any cline-yes">11x</span>
|
|
803
|
+
<span class="cline-any cline-yes">11x</span>
|
|
804
|
+
<span class="cline-any cline-yes">21x</span>
|
|
805
|
+
<span class="cline-any cline-neutral"> </span>
|
|
806
|
+
<span class="cline-any cline-no"> </span>
|
|
807
|
+
<span class="cline-any cline-yes">21x</span>
|
|
808
|
+
<span class="cline-any cline-yes">21x</span>
|
|
809
|
+
<span class="cline-any cline-neutral"> </span>
|
|
810
|
+
<span class="cline-any cline-yes">13x</span>
|
|
811
|
+
<span class="cline-any cline-yes">13x</span>
|
|
812
|
+
<span class="cline-any cline-yes">13x</span>
|
|
813
|
+
<span class="cline-any cline-neutral"> </span>
|
|
814
|
+
<span class="cline-any cline-yes">13x</span>
|
|
815
|
+
<span class="cline-any cline-yes">11x</span>
|
|
816
|
+
<span class="cline-any cline-yes">11x</span>
|
|
817
|
+
<span class="cline-any cline-yes">11x</span>
|
|
818
|
+
<span class="cline-any cline-neutral"> </span>
|
|
819
|
+
<span class="cline-any cline-no"> </span>
|
|
820
|
+
<span class="cline-any cline-yes">11x</span>
|
|
821
|
+
<span class="cline-any cline-neutral"> </span>
|
|
822
|
+
<span class="cline-any cline-yes">13x</span>
|
|
823
|
+
<span class="cline-any cline-yes">3x</span>
|
|
824
|
+
<span class="cline-any cline-yes">4x</span>
|
|
825
|
+
<span class="cline-any cline-yes">3x</span>
|
|
826
|
+
<span class="cline-any cline-yes">3x</span>
|
|
827
|
+
<span class="cline-any cline-yes">4x</span>
|
|
828
|
+
<span class="cline-any cline-neutral"> </span>
|
|
829
|
+
<span class="cline-any cline-no"> </span>
|
|
830
|
+
<span class="cline-any cline-yes">4x</span>
|
|
831
|
+
<span class="cline-any cline-yes">3x</span>
|
|
832
|
+
<span class="cline-any cline-neutral"> </span>
|
|
833
|
+
<span class="cline-any cline-neutral"> </span>
|
|
834
|
+
<span class="cline-any cline-yes">13x</span>
|
|
835
|
+
<span class="cline-any cline-yes">2x</span>
|
|
836
|
+
<span class="cline-any cline-yes">2x</span>
|
|
837
|
+
<span class="cline-any cline-yes">2x</span>
|
|
838
|
+
<span class="cline-any cline-yes">2x</span>
|
|
839
|
+
<span class="cline-any cline-yes">2x</span>
|
|
840
|
+
<span class="cline-any cline-yes">2x</span>
|
|
841
|
+
<span class="cline-any cline-neutral"> </span>
|
|
842
|
+
<span class="cline-any cline-yes">13x</span>
|
|
843
|
+
<span class="cline-any cline-no"> </span>
|
|
844
|
+
<span class="cline-any cline-no"> </span>
|
|
845
|
+
<span class="cline-any cline-neutral"> </span>
|
|
846
|
+
<span class="cline-any cline-yes">13x</span>
|
|
847
|
+
<span class="cline-any cline-no"> </span>
|
|
848
|
+
<span class="cline-any cline-no"> </span>
|
|
849
|
+
<span class="cline-any cline-yes">13x</span>
|
|
850
|
+
<span class="cline-any cline-neutral"> </span>
|
|
851
|
+
<span class="cline-any cline-neutral"> </span>
|
|
852
|
+
<span class="cline-any cline-neutral"> </span>
|
|
853
|
+
<span class="cline-any cline-neutral"> </span>
|
|
854
|
+
<span class="cline-any cline-neutral"> </span>
|
|
855
|
+
<span class="cline-any cline-neutral"> </span>
|
|
856
|
+
<span class="cline-any cline-yes">1x</span>
|
|
857
|
+
<span class="cline-any cline-yes">11x</span>
|
|
858
|
+
<span class="cline-any cline-yes">11x</span>
|
|
859
|
+
<span class="cline-any cline-yes">11x</span>
|
|
860
|
+
<span class="cline-any cline-neutral"> </span>
|
|
861
|
+
<span class="cline-any cline-yes">11x</span>
|
|
862
|
+
<span class="cline-any cline-neutral"> </span>
|
|
863
|
+
<span class="cline-any cline-yes">11x</span>
|
|
864
|
+
<span class="cline-any cline-yes">12x</span>
|
|
865
|
+
<span class="cline-any cline-yes">12x</span>
|
|
866
|
+
<span class="cline-any cline-yes">12x</span>
|
|
867
|
+
<span class="cline-any cline-yes">14x</span>
|
|
868
|
+
<span class="cline-any cline-yes">14x</span>
|
|
869
|
+
<span class="cline-any cline-yes">12x</span>
|
|
870
|
+
<span class="cline-any cline-yes">12x</span>
|
|
871
|
+
<span class="cline-any cline-yes">12x</span>
|
|
872
|
+
<span class="cline-any cline-yes">12x</span>
|
|
873
|
+
<span class="cline-any cline-yes">12x</span>
|
|
874
|
+
<span class="cline-any cline-yes">12x</span>
|
|
875
|
+
<span class="cline-any cline-neutral"> </span>
|
|
876
|
+
<span class="cline-any cline-yes">11x</span>
|
|
877
|
+
<span class="cline-any cline-yes">25x</span>
|
|
878
|
+
<span class="cline-any cline-yes">12x</span>
|
|
879
|
+
<span class="cline-any cline-yes">12x</span>
|
|
880
|
+
<span class="cline-any cline-yes">12x</span>
|
|
881
|
+
<span class="cline-any cline-neutral"> </span>
|
|
882
|
+
<span class="cline-any cline-no"> </span>
|
|
883
|
+
<span class="cline-any cline-yes">12x</span>
|
|
884
|
+
<span class="cline-any cline-yes">25x</span>
|
|
885
|
+
<span class="cline-any cline-yes">25x</span>
|
|
886
|
+
<span class="cline-any cline-neutral"> </span>
|
|
887
|
+
<span class="cline-any cline-yes">11x</span>
|
|
888
|
+
<span class="cline-any cline-yes">13x</span>
|
|
889
|
+
<span class="cline-any cline-no"> </span>
|
|
890
|
+
<span class="cline-any cline-no"> </span>
|
|
891
|
+
<span class="cline-any cline-no"> </span>
|
|
892
|
+
<span class="cline-any cline-yes">13x</span>
|
|
893
|
+
<span class="cline-any cline-yes">13x</span>
|
|
894
|
+
<span class="cline-any cline-yes">13x</span>
|
|
895
|
+
<span class="cline-any cline-no"> </span>
|
|
896
|
+
<span class="cline-any cline-no"> </span>
|
|
897
|
+
<span class="cline-any cline-yes">13x</span>
|
|
898
|
+
<span class="cline-any cline-neutral"> </span>
|
|
899
|
+
<span class="cline-any cline-yes">11x</span>
|
|
900
|
+
<span class="cline-any cline-yes">1x</span>
|
|
901
|
+
<span class="cline-any cline-yes">1x</span>
|
|
902
|
+
<span class="cline-any cline-neutral"> </span>
|
|
903
|
+
<span class="cline-any cline-neutral"> </span>
|
|
904
|
+
<span class="cline-any cline-neutral"> </span>
|
|
905
|
+
<span class="cline-any cline-neutral"> </span>
|
|
906
|
+
<span class="cline-any cline-yes">11x</span>
|
|
907
|
+
<span class="cline-any cline-yes">2x</span>
|
|
908
|
+
<span class="cline-any cline-yes">1x</span>
|
|
909
|
+
<span class="cline-any cline-yes">1x</span>
|
|
910
|
+
<span class="cline-any cline-yes">2x</span>
|
|
911
|
+
<span class="cline-any cline-neutral"> </span>
|
|
912
|
+
<span class="cline-any cline-no"> </span>
|
|
913
|
+
<span class="cline-any cline-yes">2x</span>
|
|
914
|
+
<span class="cline-any cline-yes">11x</span>
|
|
915
|
+
<span class="cline-any cline-no"> </span>
|
|
916
|
+
<span class="cline-any cline-no"> </span>
|
|
917
|
+
<span class="cline-any cline-yes">11x</span>
|
|
918
|
+
<span class="cline-any cline-neutral"> </span>
|
|
919
|
+
<span class="cline-any cline-neutral"> </span>
|
|
920
|
+
<span class="cline-any cline-neutral"> </span>
|
|
921
|
+
<span class="cline-any cline-yes">1x</span>
|
|
922
|
+
<span class="cline-any cline-yes">21x</span>
|
|
923
|
+
<span class="cline-any cline-yes">21x</span>
|
|
924
|
+
<span class="cline-any cline-yes">21x</span>
|
|
925
|
+
<span class="cline-any cline-yes">21x</span>
|
|
926
|
+
<span class="cline-any cline-neutral"> </span>
|
|
927
|
+
<span class="cline-any cline-neutral"> </span>
|
|
928
|
+
<span class="cline-any cline-neutral"> </span>
|
|
929
|
+
<span class="cline-any cline-yes">21x</span>
|
|
930
|
+
<span class="cline-any cline-yes">21x</span>
|
|
931
|
+
<span class="cline-any cline-yes">21x</span>
|
|
932
|
+
<span class="cline-any cline-yes">21x</span>
|
|
933
|
+
<span class="cline-any cline-yes">21x</span>
|
|
934
|
+
<span class="cline-any cline-yes">21x</span>
|
|
935
|
+
<span class="cline-any cline-yes">21x</span>
|
|
936
|
+
<span class="cline-any cline-yes">21x</span>
|
|
937
|
+
<span class="cline-any cline-yes">21x</span>
|
|
938
|
+
<span class="cline-any cline-neutral"> </span>
|
|
939
|
+
<span class="cline-any cline-yes">21x</span>
|
|
940
|
+
<span class="cline-any cline-yes">21x</span>
|
|
941
|
+
<span class="cline-any cline-neutral"> </span>
|
|
942
|
+
<span class="cline-any cline-yes">21x</span>
|
|
943
|
+
<span class="cline-any cline-yes">21x</span>
|
|
944
|
+
<span class="cline-any cline-neutral"> </span>
|
|
945
|
+
<span class="cline-any cline-neutral"> </span>
|
|
946
|
+
<span class="cline-any cline-neutral"> </span>
|
|
947
|
+
<span class="cline-any cline-neutral"> </span>
|
|
948
|
+
<span class="cline-any cline-neutral"> </span>
|
|
949
|
+
<span class="cline-any cline-neutral"> </span>
|
|
950
|
+
<span class="cline-any cline-neutral"> </span>
|
|
951
|
+
<span class="cline-any cline-yes">21x</span>
|
|
952
|
+
<span class="cline-any cline-yes">21x</span>
|
|
953
|
+
<span class="cline-any cline-yes">21x</span>
|
|
954
|
+
<span class="cline-any cline-neutral"> </span>
|
|
955
|
+
<span class="cline-any cline-neutral"> </span>
|
|
956
|
+
<span class="cline-any cline-neutral"> </span>
|
|
957
|
+
<span class="cline-any cline-yes">21x</span>
|
|
958
|
+
<span class="cline-any cline-yes">21x</span>
|
|
959
|
+
<span class="cline-any cline-yes">21x</span>
|
|
960
|
+
<span class="cline-any cline-yes">21x</span>
|
|
961
|
+
<span class="cline-any cline-yes">21x</span>
|
|
962
|
+
<span class="cline-any cline-yes">21x</span>
|
|
963
|
+
<span class="cline-any cline-yes">21x</span>
|
|
964
|
+
<span class="cline-any cline-yes">23x</span>
|
|
965
|
+
<span class="cline-any cline-neutral"> </span>
|
|
966
|
+
<span class="cline-any cline-yes">23x</span>
|
|
967
|
+
<span class="cline-any cline-yes">21x</span>
|
|
968
|
+
<span class="cline-any cline-yes">11x</span>
|
|
969
|
+
<span class="cline-any cline-yes">21x</span>
|
|
970
|
+
<span class="cline-any cline-yes">11x</span>
|
|
971
|
+
<span class="cline-any cline-yes">21x</span>
|
|
972
|
+
<span class="cline-any cline-yes">9x</span>
|
|
973
|
+
<span class="cline-any cline-yes">21x</span>
|
|
974
|
+
<span class="cline-any cline-yes">21x</span>
|
|
975
|
+
<span class="cline-any cline-neutral"> </span>
|
|
976
|
+
<span class="cline-any cline-neutral"> </span>
|
|
977
|
+
<span class="cline-any cline-no"> </span>
|
|
978
|
+
<span class="cline-any cline-yes">21x</span>
|
|
979
|
+
<span class="cline-any cline-yes">21x</span>
|
|
980
|
+
<span class="cline-any cline-yes">21x</span>
|
|
981
|
+
<span class="cline-any cline-yes">21x</span>
|
|
982
|
+
<span class="cline-any cline-no"> </span>
|
|
983
|
+
<span class="cline-any cline-no"> </span>
|
|
984
|
+
<span class="cline-any cline-no"> </span>
|
|
985
|
+
<span class="cline-any cline-no"> </span>
|
|
986
|
+
<span class="cline-any cline-yes">21x</span>
|
|
987
|
+
<span class="cline-any cline-neutral"> </span>
|
|
988
|
+
<span class="cline-any cline-neutral"> </span>
|
|
989
|
+
<span class="cline-any cline-yes">21x</span>
|
|
990
|
+
<span class="cline-any cline-yes">48x</span>
|
|
991
|
+
<span class="cline-any cline-no"> </span>
|
|
992
|
+
<span class="cline-any cline-no"> </span>
|
|
993
|
+
<span class="cline-any cline-no"> </span>
|
|
994
|
+
<span class="cline-any cline-no"> </span>
|
|
995
|
+
<span class="cline-any cline-yes">48x</span>
|
|
996
|
+
<span class="cline-any cline-yes">48x</span>
|
|
997
|
+
<span class="cline-any cline-neutral"> </span>
|
|
998
|
+
<span class="cline-any cline-yes">21x</span>
|
|
999
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1000
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1001
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1002
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1003
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1004
|
+
<span class="cline-any cline-yes">24x</span>
|
|
1005
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1006
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1007
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1008
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1009
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1010
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1011
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1012
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1013
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1014
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1015
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1016
|
+
<span class="cline-any cline-yes">22x</span>
|
|
1017
|
+
<span class="cline-any cline-yes">24x</span>
|
|
1018
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1019
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1020
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1021
|
+
<span class="cline-any cline-yes">24x</span>
|
|
1022
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1023
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1024
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1025
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1026
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1027
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1028
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1029
|
+
<span class="cline-any cline-no"> </span>
|
|
1030
|
+
<span class="cline-any cline-no"> </span>
|
|
1031
|
+
<span class="cline-any cline-no"> </span>
|
|
1032
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1033
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1034
|
+
<span class="cline-any cline-yes">20x</span>
|
|
1035
|
+
<span class="cline-any cline-yes">20x</span>
|
|
1036
|
+
<span class="cline-any cline-yes">20x</span>
|
|
1037
|
+
<span class="cline-any cline-yes">20x</span>
|
|
1038
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1039
|
+
<span class="cline-any cline-no"> </span>
|
|
1040
|
+
<span class="cline-any cline-no"> </span>
|
|
1041
|
+
<span class="cline-any cline-no"> </span>
|
|
1042
|
+
<span class="cline-any cline-no"> </span>
|
|
1043
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1044
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1045
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1046
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1047
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1048
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1049
|
+
<span class="cline-any cline-no"> </span>
|
|
1050
|
+
<span class="cline-any cline-no"> </span>
|
|
1051
|
+
<span class="cline-any cline-no"> </span>
|
|
1052
|
+
<span class="cline-any cline-no"> </span>
|
|
1053
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1054
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1055
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1056
|
+
<span class="cline-any cline-no"> </span>
|
|
1057
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1058
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1059
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1060
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1061
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1062
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1063
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1064
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1065
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1066
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1067
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1068
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1069
|
+
<span class="cline-any cline-no"> </span>
|
|
1070
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1071
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1072
|
+
<span class="cline-any cline-no"> </span>
|
|
1073
|
+
<span class="cline-any cline-no"> </span>
|
|
1074
|
+
<span class="cline-any cline-no"> </span>
|
|
1075
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1076
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1077
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1078
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1079
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1080
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1081
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1082
|
+
<span class="cline-any cline-no"> </span>
|
|
1083
|
+
<span class="cline-any cline-no"> </span>
|
|
1084
|
+
<span class="cline-any cline-no"> </span>
|
|
1085
|
+
<span class="cline-any cline-no"> </span>
|
|
1086
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1087
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1088
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1089
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1090
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1091
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1092
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1093
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1094
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1095
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1096
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1097
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1098
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1099
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1100
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1101
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1102
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1103
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1104
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1105
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1106
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1107
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1108
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1109
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1110
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1111
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1112
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1113
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1114
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1115
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1116
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1117
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1118
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1119
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1120
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1121
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1122
|
+
<span class="cline-any cline-no"> </span>
|
|
1123
|
+
<span class="cline-any cline-no"> </span>
|
|
1124
|
+
<span class="cline-any cline-no"> </span>
|
|
1125
|
+
<span class="cline-any cline-no"> </span>
|
|
1126
|
+
<span class="cline-any cline-no"> </span>
|
|
1127
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1128
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1129
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1130
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1131
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1132
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1133
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1134
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1135
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1136
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1137
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1138
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1139
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1140
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1141
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1142
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1143
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1144
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1145
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1146
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1147
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1148
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1149
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1150
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1151
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1152
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1153
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1154
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1155
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1156
|
+
<span class="cline-any cline-no"> </span>
|
|
1157
|
+
<span class="cline-any cline-no"> </span>
|
|
1158
|
+
<span class="cline-any cline-no"> </span>
|
|
1159
|
+
<span class="cline-any cline-no"> </span>
|
|
1160
|
+
<span class="cline-any cline-yes">13x</span>
|
|
1161
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1162
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1163
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1164
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1165
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1166
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1167
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1168
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1169
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1170
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1171
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1172
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1173
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1174
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1175
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1176
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1177
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1178
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1179
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1180
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1181
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1182
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1183
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1184
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1185
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1186
|
+
<span class="cline-any cline-yes">15x</span>
|
|
1187
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1188
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1189
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1190
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1191
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1192
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1193
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1194
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1195
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1196
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1197
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1198
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1199
|
+
<span class="cline-any cline-yes">14x</span>
|
|
1200
|
+
<span class="cline-any cline-yes">14x</span>
|
|
1201
|
+
<span class="cline-any cline-yes">14x</span>
|
|
1202
|
+
<span class="cline-any cline-yes">14x</span>
|
|
1203
|
+
<span class="cline-any cline-yes">14x</span>
|
|
1204
|
+
<span class="cline-any cline-yes">14x</span>
|
|
1205
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1206
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1207
|
+
<span class="cline-any cline-yes">4x</span>
|
|
1208
|
+
<span class="cline-any cline-yes">4x</span>
|
|
1209
|
+
<span class="cline-any cline-yes">4x</span>
|
|
1210
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1211
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1212
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1213
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1214
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1215
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1216
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1217
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1218
|
+
<span class="cline-any cline-no"> </span>
|
|
1219
|
+
<span class="cline-any cline-no"> </span>
|
|
1220
|
+
<span class="cline-any cline-no"> </span>
|
|
1221
|
+
<span class="cline-any cline-no"> </span>
|
|
1222
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1223
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1224
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1225
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1226
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1227
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1228
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1229
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1230
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1231
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1232
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1233
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1234
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1235
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1236
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1237
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1238
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1239
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1240
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1241
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1242
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1243
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1244
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1245
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1246
|
+
<span class="cline-any cline-no"> </span>
|
|
1247
|
+
<span class="cline-any cline-no"> </span>
|
|
1248
|
+
<span class="cline-any cline-no"> </span>
|
|
1249
|
+
<span class="cline-any cline-no"> </span>
|
|
1250
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1251
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1252
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1253
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1254
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1255
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1256
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1257
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1258
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1259
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1260
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1261
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1262
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1263
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1264
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1265
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1266
|
+
<span class="cline-any cline-yes">12x</span>
|
|
1267
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1268
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1269
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1270
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1271
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1272
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1273
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1274
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1275
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1276
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1277
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1278
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1279
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1280
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1281
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1282
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1283
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1284
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1285
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1286
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1287
|
+
<span class="cline-any cline-yes">1x</span>
|
|
1288
|
+
<span class="cline-any cline-no"> </span>
|
|
1289
|
+
<span class="cline-any cline-yes">12x</span>
|
|
1290
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1291
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1292
|
+
<span class="cline-any cline-yes">11x</span>
|
|
1293
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1294
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1295
|
+
<span class="cline-any cline-yes">9x</span>
|
|
1296
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1297
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1298
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1299
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1300
|
+
<span class="cline-any cline-yes">2x</span>
|
|
1301
|
+
<span class="cline-any cline-yes">9x</span>
|
|
1302
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1303
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1304
|
+
<span class="cline-any cline-no"> </span>
|
|
1305
|
+
<span class="cline-any cline-no"> </span>
|
|
1306
|
+
<span class="cline-any cline-no"> </span>
|
|
1307
|
+
<span class="cline-any cline-no"> </span>
|
|
1308
|
+
<span class="cline-any cline-no"> </span>
|
|
1309
|
+
<span class="cline-any cline-no"> </span>
|
|
1310
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1311
|
+
<span class="cline-any cline-no"> </span>
|
|
1312
|
+
<span class="cline-any cline-no"> </span>
|
|
1313
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1314
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1315
|
+
<span class="cline-any cline-no"> </span>
|
|
1316
|
+
<span class="cline-any cline-no"> </span>
|
|
1317
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1318
|
+
<span class="cline-any cline-no"> </span>
|
|
1319
|
+
<span class="cline-any cline-no"> </span>
|
|
1320
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1321
|
+
<span class="cline-any cline-no"> </span>
|
|
1322
|
+
<span class="cline-any cline-no"> </span>
|
|
1323
|
+
<span class="cline-any cline-neutral"> </span>
|
|
1324
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1325
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1326
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1327
|
+
<span class="cline-any cline-yes">9x</span>
|
|
1328
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1329
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1330
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1331
|
+
<span class="cline-any cline-yes">21x</span>
|
|
1332
|
+
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">/**
|
|
1333
|
+
* TabsCoordinator: the per-tab role state machine for shared-tabs mode. Sits
|
|
1334
|
+
* between the broker client (election events, ports) and the rest of the
|
|
1335
|
+
* client (engine + sync), which it drives exclusively through
|
|
1336
|
+
* {@link CoordinatorHooks} so this module depends on neither concrete class.
|
|
1337
|
+
*
|
|
1338
|
+
* Roles:
|
|
1339
|
+
* - leader: owns the sqlite worker (OPFS) and the sync loop; serves follower
|
|
1340
|
+
* ports handed over by the broker.
|
|
1341
|
+
* - follower: LocalStore ops go over dbPort into the leader's worker; sync
|
|
1342
|
+
* concerns are forwarded over syncPort (see {@link SyncForwarder}).
|
|
1343
|
+
* - solo: broker unavailable/rejected; exactly the pre-shared-tabs behavior.
|
|
1344
|
+
*
|
|
1345
|
+
* Bucket switches are namespace moves: `moveToBucket` re-hellos under the new
|
|
1346
|
+
* bucketId and the ordinary election machinery assigns the new role there.
|
|
1347
|
+
*/
|
|
1348
|
+
import type { Logger } from '../logger/index';
|
|
1349
|
+
import type { StorageHealth } from '../../types';
|
|
1350
|
+
import { TabBrokerClient } from './broker-client';
|
|
1351
|
+
import { acquireLeaderTabLock, type LeaderLockHandle } from './leader-locks';
|
|
1352
|
+
import {
|
|
1353
|
+
tabLockName,
|
|
1354
|
+
workerLockName,
|
|
1355
|
+
type FollowerToLeaderMessage,
|
|
1356
|
+
type IngestTuple,
|
|
1357
|
+
type LeaderToFollowerMessage,
|
|
1358
|
+
type TabId,
|
|
1359
|
+
type TabRole,
|
|
1360
|
+
} from './protocol';
|
|
1361
|
+
|
|
1362
|
+
/** How long boot waits for a role (election + attach) before going solo. */
|
|
1363
|
+
const START_TIMEOUT_MS = 15_000;
|
|
1364
|
+
|
|
1365
|
+
export interface CoordinatorHooks {
|
|
1366
|
+
/** Become the store owner: spawn the worker, open the pool under the given
|
|
1367
|
+
* per-leadership lock. Returns the resulting health for db-ready relays.
|
|
1368
|
+
* `resumeHeld` = keep the existing live worker (broker-restart path). */
|
|
1369
|
+
adoptOwner(
|
|
1370
|
+
bucketId: string,
|
|
1371
|
+
opts: { workerLockName: string; allowMemoryFallback: boolean; forceTakeover: boolean; resumeHeld: boolean }
|
|
1372
|
+
): Promise<StorageHealth>;
|
|
1373
|
+
/** Attach to a leader's worker through `dbPort`. */
|
|
1374
|
+
adoptAttached(
|
|
1375
|
+
dbPort: MessagePort,
|
|
1376
|
+
snapshot: { bucketId: string; storageHealth: StorageHealth; leadershipId: number }
|
|
1377
|
+
): Promise<void>;
|
|
1378
|
+
/** We were leader and got demoted (zombie thaw / stale promotion): tear the
|
|
1379
|
+
* owned worker down so its SAH handles free up. */
|
|
1380
|
+
releaseOwnership(): Promise<void>;
|
|
1381
|
+
/** The leader (or its port) is gone; queue/park ops until a new role lands. */
|
|
1382
|
+
onLeaderLost(reason: string): void;
|
|
1383
|
+
/** Leader side: forward a follower's dbPort into the owned worker. */
|
|
1384
|
+
exposeClientPort(clientId: string, port: MessagePort): Promise<void>;
|
|
1385
|
+
removeClientPort(clientId: string): Promise<void>;
|
|
1386
|
+
/** Sync layer role changes (implemented in the sync module). */
|
|
1387
|
+
/** Synchronous leader WIRING. Must not block: the role is published and
|
|
1388
|
+
* `leader-ready` is sent right after it returns. */
|
|
1389
|
+
becomeSyncLeader(hub: LeaderSyncHub): void;
|
|
1390
|
+
/** The blocking half of the leader handover (outbox drain, LIVE restart),
|
|
1391
|
+
* run in the background once the tab is already a working leader. */
|
|
1392
|
+
resumeSyncLeaderDuties(): Promise<void>;
|
|
1393
|
+
becomeSyncFollower(forwarder: SyncForwarder): void;
|
|
1394
|
+
becomeSyncSolo(): void;
|
|
1395
|
+
/** Current storage health, for db-ready sent to late-joining followers. */
|
|
1396
|
+
currentStorageHealth(): StorageHealth;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// ---- leader side: per-follower syncPort hub ---------------------------------
|
|
1400
|
+
|
|
1401
|
+
export interface FollowerChannel {
|
|
1402
|
+
tabId: TabId;
|
|
1403
|
+
send(msg: LeaderToFollowerMessage): void;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/** Leader-side fan-out surface handed to the sync layer. The sync router
|
|
1407
|
+
* (modules/sync/tab-router.ts) registers itself as the message handler. */
|
|
1408
|
+
export class LeaderSyncHub {
|
|
1409
|
+
private followers = new Map<TabId, MessagePort>();
|
|
1410
|
+
private seq = 0;
|
|
1411
|
+
onFollowerMessage:
|
|
1412
|
+
| ((tabId: TabId, msg: FollowerToLeaderMessage) => void)
|
|
1413
|
+
| null = null;
|
|
1414
|
+
onFollowerDetached: ((tabId: TabId) => void) | null = null;
|
|
1415
|
+
|
|
1416
|
+
constructor(
|
|
1417
|
+
readonly leadershipId: number,
|
|
1418
|
+
private logger: Logger
|
|
1419
|
+
) {}
|
|
1420
|
+
|
|
1421
|
+
attach(tabId: TabId, port: MessagePort): void {
|
|
1422
|
+
this.detach(tabId);
|
|
1423
|
+
this.followers.set(tabId, port);
|
|
1424
|
+
port.onmessage = (ev: MessageEvent) => {
|
|
1425
|
+
this.onFollowerMessage?.(tabId, ev.data as FollowerToLeaderMessage);
|
|
1426
|
+
};
|
|
1427
|
+
port.onmessageerror = <span class="fstat-no" title="function not covered" >() => this.detach(tabId);</span>
|
|
1428
|
+
port.start?.();
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
detach(tabId: TabId): void {
|
|
1432
|
+
const port = this.followers.get(tabId);
|
|
1433
|
+
if (!port) return;
|
|
1434
|
+
this.followers.delete(tabId);
|
|
1435
|
+
try {
|
|
1436
|
+
port.close();
|
|
1437
|
+
<span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
|
|
1438
|
+
/* ignore */
|
|
1439
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1440
|
+
this.<span class="branch-0 cbranch-no" title="branch not covered" >onFollowerDetached?.(tabId);</span>
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
detachAll(): void {
|
|
1444
|
+
for (const tabId of [...this.followers.keys()]) this.detach(tabId);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
sendTo(tabId: TabId, msg: LeaderToFollowerMessage): void {
|
|
1448
|
+
try {
|
|
1449
|
+
this.followers.get(tabId)?.postMessage(msg);
|
|
1450
|
+
<span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
|
|
1451
|
+
/* dead port; broker re-mints */
|
|
1452
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
broadcast(msg: LeaderToFollowerMessage, exceptTabId?: TabId): void {
|
|
1456
|
+
for (const [tabId, port] of this.followers) {
|
|
1457
|
+
if (tabId === exceptTabId) continue;
|
|
1458
|
+
try {
|
|
1459
|
+
port.postMessage(msg);
|
|
1460
|
+
<span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
|
|
1461
|
+
/* ignore */
|
|
1462
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
/** Stamped ingest relay; seq lets followers detect gaps. */
|
|
1467
|
+
relayIngest(tuples: IngestTuple[], exceptTabId?: TabId): void {
|
|
1468
|
+
if (this.followers.size === 0) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
|
|
1469
|
+
this.broadcast(
|
|
1470
|
+
{ type: 'ingest-relay', tuples, leadershipId: this.leadershipId, seq: ++this.seq },
|
|
1471
|
+
exceptTabId
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
<span class="fstat-no" title="function not covered" > get followerCount(): number {</span>
|
|
1476
|
+
<span class="cstat-no" title="statement not covered" > return this.followers.size;</span>
|
|
1477
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1478
|
+
|
|
1479
|
+
<span class="fstat-no" title="function not covered" > get relayedBatches(): number {</span>
|
|
1480
|
+
<span class="cstat-no" title="statement not covered" > return this.seq;</span>
|
|
1481
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// ---- follower side: syncPort forwarder ---------------------------------------
|
|
1485
|
+
|
|
1486
|
+
/** Follower half of the syncPort. Queues while detached (leaderless window)
|
|
1487
|
+
* and flushes on rebind; a lost-in-flight mutation notify is additionally
|
|
1488
|
+
* backstopped by the new leader reloading the shared outbox from the store. */
|
|
1489
|
+
export class SyncForwarder {
|
|
1490
|
+
private port: MessagePort | null = null;
|
|
1491
|
+
private queued: FollowerToLeaderMessage[] = [];
|
|
1492
|
+
onLeaderMessage: ((msg: LeaderToFollowerMessage) => void) | null = null;
|
|
1493
|
+
|
|
1494
|
+
constructor(private tabId: TabId) {}
|
|
1495
|
+
|
|
1496
|
+
rebind(port: MessagePort): void {
|
|
1497
|
+
this.unbind();
|
|
1498
|
+
this.port = port;
|
|
1499
|
+
port.onmessage = (ev: MessageEvent) => {
|
|
1500
|
+
this.onLeaderMessage?.(ev.data as LeaderToFollowerMessage);
|
|
1501
|
+
};
|
|
1502
|
+
port.start?.();
|
|
1503
|
+
this.post({ type: 'sync-hello', tabId: this.tabId });
|
|
1504
|
+
const backlog = this.queued;
|
|
1505
|
+
this.queued = [];
|
|
1506
|
+
for (const msg of backlog) <span class="branch-0 cbranch-no" title="branch not covered" >this.post(msg);</span>
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
unbind(): void {
|
|
1510
|
+
if (this.port) {
|
|
1511
|
+
try {
|
|
1512
|
+
this.port.close();
|
|
1513
|
+
<span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
|
|
1514
|
+
/* ignore */
|
|
1515
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1516
|
+
}
|
|
1517
|
+
this.port = null;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
private post(msg: FollowerToLeaderMessage): void {
|
|
1521
|
+
if (!this.port) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
|
|
1522
|
+
<span class="cstat-no" title="statement not covered" > this.queued.push(msg);</span>
|
|
1523
|
+
<span class="cstat-no" title="statement not covered" > return;</span>
|
|
1524
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1525
|
+
try {
|
|
1526
|
+
this.port.postMessage(msg);
|
|
1527
|
+
<span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
|
|
1528
|
+
<span class="cstat-no" title="statement not covered" > this.queued.push(msg);</span>
|
|
1529
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
mutationEnqueued(mutationId: string): void {
|
|
1533
|
+
this.post({ type: 'mutation-enqueued', mutationId });
|
|
1534
|
+
}
|
|
1535
|
+
/** An optimistic write this tab just ingested. Deliberately NOT queued while
|
|
1536
|
+
* detached: a new leader primes its circuit from the shared store, which
|
|
1537
|
+
* already holds the row, and replaying a stale tuple at it later would put
|
|
1538
|
+
* an older `_00_rv` in its version memo. */
|
|
1539
|
+
ingest(tuples: IngestTuple[]): void {
|
|
1540
|
+
if (!this.port) return;
|
|
1541
|
+
try {
|
|
1542
|
+
this.port.postMessage({ type: 'ingest', tuples });
|
|
1543
|
+
<span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
|
|
1544
|
+
/* dead port; broker re-mints */
|
|
1545
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1546
|
+
}
|
|
1547
|
+
<span class="fstat-no" title="function not covered" > requestPoll(): void {</span>
|
|
1548
|
+
<span class="cstat-no" title="statement not covered" > this.post({ type: 'request-poll' });</span>
|
|
1549
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// ---- the coordinator ----------------------------------------------------------
|
|
1553
|
+
|
|
1554
|
+
export class TabsCoordinator {
|
|
1555
|
+
role: TabRole = 'solo';
|
|
1556
|
+
leadershipId = 0;
|
|
1557
|
+
leaderTabId: TabId | null = null;
|
|
1558
|
+
brokerRole: 'pending' | TabRole = 'pending';
|
|
1559
|
+
/** Wall time from `become-leader` to `leader-ready` on the last promotion.
|
|
1560
|
+
* Reported in the DevTools role-change event: a slow store adopt (OPFS pool
|
|
1561
|
+
* contention after the previous leader died) shows up here as seconds. */
|
|
1562
|
+
lastPromotionMs: number | null = null;
|
|
1563
|
+
private fingerprint: string;
|
|
1564
|
+
private bucketId: string;
|
|
1565
|
+
private broker: TabBrokerClient;
|
|
1566
|
+
private hub: LeaderSyncHub | null = null;
|
|
1567
|
+
private forwarder: SyncForwarder | null = null;
|
|
1568
|
+
private roleListeners = new Set<(role: TabRole) => void>();
|
|
1569
|
+
private startResolve: ((role: TabRole) => void) | null = null;
|
|
1570
|
+
private startReject: ((e: Error) => void) | null = null;
|
|
1571
|
+
/** Ports received before start() resolves or between roles. */
|
|
1572
|
+
private promotionChain: Promise<void> = Promise.resolve();
|
|
1573
|
+
private closed = false;
|
|
1574
|
+
|
|
1575
|
+
constructor(
|
|
1576
|
+
private deps: {
|
|
1577
|
+
tabId: TabId;
|
|
1578
|
+
fingerprint: string;
|
|
1579
|
+
hooks: CoordinatorHooks;
|
|
1580
|
+
logger: Logger;
|
|
1581
|
+
/** Fired on pagehide while this tab leads: last-chance OPFS release. */
|
|
1582
|
+
onLeaderPageHide?: () => void;
|
|
1583
|
+
}
|
|
1584
|
+
) {
|
|
1585
|
+
this.fingerprint = deps.fingerprint;
|
|
1586
|
+
this.bucketId = 'anon';
|
|
1587
|
+
// The URL is built HERE (same directory as the worker source) so the
|
|
1588
|
+
// published flat bundle's rewritten './tabs-broker-worker.js' resolves at
|
|
1589
|
+
// the dist top level, exactly like the sqlite worker URL does.
|
|
1590
|
+
this.broker = new TabBrokerClient(
|
|
1591
|
+
new URL('./tabs-broker-worker.ts', import.meta.url),
|
|
1592
|
+
deps.tabId,
|
|
1593
|
+
{
|
|
1594
|
+
onBecomeLeader: (msg) => this.enqueue(() => this.promote(msg)),
|
|
1595
|
+
onDemote: (leadershipId) => this.enqueue(() => this.demote(leadershipId)),
|
|
1596
|
+
onLeaderReady: (leadershipId, leaderTabId) => {
|
|
1597
|
+
this.leaderTabId = leaderTabId;
|
|
1598
|
+
void leadershipId;
|
|
1599
|
+
},
|
|
1600
|
+
onAttachFollowerPorts: (followerTabId, leadershipId, dbPort, syncPort) =>
|
|
1601
|
+
this.enqueue(() => this.serveFollower(followerTabId, leadershipId, dbPort, syncPort)),
|
|
1602
|
+
onUseFollowerPorts: (leaderTabId, leadershipId, dbPort, syncPort) =>
|
|
1603
|
+
this.enqueue(() => this.attachToLeader(leaderTabId, leadershipId, dbPort, syncPort)),
|
|
1604
|
+
onCloseFollowerPorts: (leadershipId) =>
|
|
1605
|
+
this.enqueue(() => this.handleLeaderGone(leadershipId)),
|
|
1606
|
+
onUnsupported: <span class="fstat-no" title="function not covered" >() => this.fallbackToSolo('broker rejected this tab'),</span>
|
|
1607
|
+
onBrokerRestarted: <span class="fstat-no" title="function not covered" >() => {</span>
|
|
1608
|
+
// Direct MessageChannels survive a broker restart; roles get
|
|
1609
|
+
// re-confirmed by the fresh election (heldLeadership fast path).
|
|
1610
|
+
<span class="cstat-no" title="statement not covered" > },</span>
|
|
1611
|
+
},
|
|
1612
|
+
deps.logger
|
|
1613
|
+
);
|
|
1614
|
+
if (typeof window !== <span class="branch-0 cbranch-no" title="branch not covered" >'undefined' && deps.onLeaderPageHide) <span class="branch-0 cbranch-no" title="branch not covered" >{</span></span>
|
|
1615
|
+
<span class="cstat-no" title="statement not covered" > window.addEventListener('pagehide', () => {</span>
|
|
1616
|
+
<span class="cstat-no" title="statement not covered" > if (this.role === 'leader') deps.onLeaderPageHide?.();</span>
|
|
1617
|
+
<span class="cstat-no" title="statement not covered" > });</span>
|
|
1618
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/** Serialize role transitions; each is small but async (worker opens). */
|
|
1622
|
+
private enqueue(fn: () => Promise<void>): void {
|
|
1623
|
+
this.promotionChain = this.promotionChain.then(fn, fn).catch((e) => {
|
|
1624
|
+
<span class="cstat-no" title="statement not covered" > this.deps.logger.error(</span>
|
|
1625
|
+
<span class="cstat-no" title="statement not covered" > { err: e, Category: 'sp00ky-client::TabsCoordinator' },</span>
|
|
1626
|
+
<span class="cstat-no" title="statement not covered" > 'Role transition failed'</span>
|
|
1627
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
onRoleChange(cb: (role: TabRole) => void): () => void {
|
|
1632
|
+
this.roleListeners.add(cb);
|
|
1633
|
+
return () => this.roleListeners.delete(cb);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
private setRole(role: TabRole): void {
|
|
1637
|
+
if (this.role !== role) {
|
|
1638
|
+
this.role = role;
|
|
1639
|
+
this.deps.logger.info(
|
|
1640
|
+
{
|
|
1641
|
+
role,
|
|
1642
|
+
leadershipId: this.leadershipId,
|
|
1643
|
+
promotionMs: role === 'leader' ? this.lastPromotionMs : undefined,
|
|
1644
|
+
Category: 'sp00ky-client::TabsCoordinator',
|
|
1645
|
+
},
|
|
1646
|
+
'Tab role changed'
|
|
1647
|
+
);
|
|
1648
|
+
for (const cb of this.roleListeners) cb(role);
|
|
1649
|
+
}
|
|
1650
|
+
if (this.startResolve && role !== 'solo') {
|
|
1651
|
+
this.startResolve(role);
|
|
1652
|
+
this.startResolve = null;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/** Connect the broker and resolve once this tab has a usable store role.
|
|
1657
|
+
* Rejects when no role lands in time; the caller then boots solo. */
|
|
1658
|
+
start(bucketId: string): Promise<TabRole> {
|
|
1659
|
+
this.bucketId = bucketId;
|
|
1660
|
+
return new Promise<TabRole>((resolve, reject) => {
|
|
1661
|
+
const timeout = setTimeout(() => {
|
|
1662
|
+
<span class="cstat-no" title="statement not covered" > this.startResolve = null;</span>
|
|
1663
|
+
<span class="cstat-no" title="statement not covered" > this.startReject = null;</span>
|
|
1664
|
+
<span class="cstat-no" title="statement not covered" > reject(new Error('shared-tabs: no role assigned in time'));</span>
|
|
1665
|
+
}, START_TIMEOUT_MS);
|
|
1666
|
+
this.startResolve = (role) => {
|
|
1667
|
+
clearTimeout(timeout);
|
|
1668
|
+
this.startReject = null;
|
|
1669
|
+
resolve(role);
|
|
1670
|
+
};
|
|
1671
|
+
this.startReject = <span class="fstat-no" title="function not covered" >(e) => {</span>
|
|
1672
|
+
<span class="cstat-no" title="statement not covered" > clearTimeout(timeout);</span>
|
|
1673
|
+
<span class="cstat-no" title="statement not covered" > this.startResolve = null;</span>
|
|
1674
|
+
<span class="cstat-no" title="statement not covered" > reject(e);</span>
|
|
1675
|
+
<span class="cstat-no" title="statement not covered" > };</span>
|
|
1676
|
+
this.broker
|
|
1677
|
+
.connect({
|
|
1678
|
+
fingerprint: this.fingerprint,
|
|
1679
|
+
bucketId,
|
|
1680
|
+
heldLeadership: () =>
|
|
1681
|
+
this.role === <span class="branch-0 cbranch-no" title="branch not covered" >'leader'</span>
|
|
1682
|
+
<span class="cstat-no" title="statement not covered" > ? {</span>
|
|
1683
|
+
<span class="cstat-no" title="statement not covered" > leadershipId: this.leadershipId,</span>
|
|
1684
|
+
<span class="cstat-no" title="statement not covered" > workerLockName: workerLockName(this.fingerprint, this.bucketId, this.leadershipId),</span>
|
|
1685
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1686
|
+
: null,
|
|
1687
|
+
})
|
|
1688
|
+
.catch((e) => {
|
|
1689
|
+
<span class="cstat-no" title="statement not covered" > this.startReject?.(e instanceof Error ? e : new Error(String(e)));</span>
|
|
1690
|
+
});
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
/** Bucket switch: leave the old namespace, join the new one. Resolves when
|
|
1695
|
+
* a role lands in the new namespace. */
|
|
1696
|
+
moveToBucket(bucketId: string): Promise<TabRole> {
|
|
1697
|
+
return new Promise<TabRole>((resolve, reject) => {
|
|
1698
|
+
this.enqueue(async () => {
|
|
1699
|
+
// Tear down the old role locally; the broker's rehello handling evicts
|
|
1700
|
+
// us from the old namespace and re-elects there.
|
|
1701
|
+
if (this.role === 'leader') await this.teardownLeader(<span class="branch-0 cbranch-no" title="branch not covered" >);</span>
|
|
1702
|
+
<span class="cstat-no" title="statement not covered" > else if (this.role === 'follower') this.teardownFollower('bucket switch');</span>
|
|
1703
|
+
this.bucketId = bucketId;
|
|
1704
|
+
const timeout = setTimeout(() => {
|
|
1705
|
+
<span class="cstat-no" title="statement not covered" > this.startResolve = null;</span>
|
|
1706
|
+
<span class="cstat-no" title="statement not covered" > this.startReject = null;</span>
|
|
1707
|
+
<span class="cstat-no" title="statement not covered" > reject(new Error('shared-tabs: no role assigned after bucket switch'));</span>
|
|
1708
|
+
}, START_TIMEOUT_MS);
|
|
1709
|
+
this.startResolve = (role) => {
|
|
1710
|
+
clearTimeout(timeout);
|
|
1711
|
+
this.startReject = null;
|
|
1712
|
+
resolve(role);
|
|
1713
|
+
};
|
|
1714
|
+
this.startReject = <span class="fstat-no" title="function not covered" >(e) => {</span>
|
|
1715
|
+
<span class="cstat-no" title="statement not covered" > clearTimeout(timeout);</span>
|
|
1716
|
+
<span class="cstat-no" title="statement not covered" > this.startResolve = null;</span>
|
|
1717
|
+
<span class="cstat-no" title="statement not covered" > reject(e);</span>
|
|
1718
|
+
<span class="cstat-no" title="statement not covered" > };</span>
|
|
1719
|
+
await this.broker.rehello({
|
|
1720
|
+
fingerprint: this.fingerprint,
|
|
1721
|
+
bucketId,
|
|
1722
|
+
heldLeadership: () => null,
|
|
1723
|
+
});
|
|
1724
|
+
});
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
// ---- transitions -----------------------------------------------------------
|
|
1729
|
+
|
|
1730
|
+
private async promote(msg: {
|
|
1731
|
+
leadershipId: number;
|
|
1732
|
+
forceTakeover: boolean;
|
|
1733
|
+
allowMemoryFallback: boolean;
|
|
1734
|
+
resumeHeld: boolean;
|
|
1735
|
+
}): Promise<void> {
|
|
1736
|
+
if (this.closed) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
|
|
1737
|
+
if (msg.leadershipId <= this.<span class="branch-0 cbranch-no" title="branch not covered" >leadershipId && !msg.resumeHeld) <span class="branch-0 cbranch-no" title="branch not covered" >r</span>eturn;</span>
|
|
1738
|
+
const previousRole = this.role;
|
|
1739
|
+
const startedAt = Date.now();
|
|
1740
|
+
try {
|
|
1741
|
+
if (previousRole === 'follower') this.teardownFollower('promoted');
|
|
1742
|
+
this.leadershipId = msg.leadershipId;
|
|
1743
|
+
// The tab lock is the broker's CRASH detector: it queues a request on
|
|
1744
|
+
// this name, and being granted means this tab died (locks release on tab
|
|
1745
|
+
// death instantly, unlike the 15s pong timeout). Steal only when the
|
|
1746
|
+
// broker said the previous holder is a frozen zombie.
|
|
1747
|
+
const lock = await acquireLeaderTabLock(tabLockName(this.fingerprint, this.bucketId), {
|
|
1748
|
+
steal: msg.forceTakeover,
|
|
1749
|
+
});
|
|
1750
|
+
if (!lock) <span class="branch-0 cbranch-no" title="branch not covered" >throw new Error('leader tab lock unavailable');</span>
|
|
1751
|
+
this.<span class="branch-0 cbranch-no" title="branch not covered" >tabLock?.release()</span>;
|
|
1752
|
+
this.tabLock = lock;
|
|
1753
|
+
lock.onLost(() => {
|
|
1754
|
+
// Stolen from under us (we were presumed dead): resign.
|
|
1755
|
+
<span class="cstat-no" title="statement not covered" > this.enqueue(async () => {</span>
|
|
1756
|
+
<span class="cstat-no" title="statement not covered" > if (this.leadershipId !== msg.leadershipId || this.role !== 'leader') return;</span>
|
|
1757
|
+
<span class="cstat-no" title="statement not covered" > await this.teardownLeader();</span>
|
|
1758
|
+
<span class="cstat-no" title="statement not covered" > this.deps.hooks.onLeaderLost('tab lock stolen');</span>
|
|
1759
|
+
<span class="cstat-no" title="statement not covered" > });</span>
|
|
1760
|
+
});
|
|
1761
|
+
const health = await this.deps.hooks.adoptOwner(this.bucketId, {
|
|
1762
|
+
workerLockName: workerLockName(this.fingerprint, this.bucketId, msg.leadershipId),
|
|
1763
|
+
allowMemoryFallback: msg.allowMemoryFallback,
|
|
1764
|
+
forceTakeover: msg.forceTakeover,
|
|
1765
|
+
resumeHeld: msg.resumeHeld,
|
|
1766
|
+
});
|
|
1767
|
+
void health;
|
|
1768
|
+
this.hub = new LeaderSyncHub(msg.leadershipId, this.deps.logger);
|
|
1769
|
+
// Owning the store IS being the leader: it unparks this tab's ops and is
|
|
1770
|
+
// everything a follower's ports need. Publish the role and tell the
|
|
1771
|
+
// broker BEFORE any sync work. Waiting on the outbox drain and the LIVE
|
|
1772
|
+
// restart here once left a tab that owned the OPFS pool but never sent
|
|
1773
|
+
// `leader-ready`, so the broker minted no follower ports and re-elected
|
|
1774
|
+
// no one (`assignFollowerPorts` and `electIfNeeded` both bail on that
|
|
1775
|
+
// state) and the whole namespace stayed leaderless until a reload. Those
|
|
1776
|
+
// duties are best-effort anyway: the list_ref poll covers a missing LIVE.
|
|
1777
|
+
this.deps.hooks.becomeSyncLeader(this.hub);
|
|
1778
|
+
this.lastPromotionMs = Date.now() - startedAt;
|
|
1779
|
+
this.setRole('leader');
|
|
1780
|
+
this.broker.send({
|
|
1781
|
+
type: 'leader-ready',
|
|
1782
|
+
tabId: this.deps.tabId,
|
|
1783
|
+
bucketId: this.bucketId,
|
|
1784
|
+
leadershipId: msg.leadershipId,
|
|
1785
|
+
});
|
|
1786
|
+
// Off the promotion chain on purpose: a hang here must not block the
|
|
1787
|
+
// next role transition (demote, bucket switch) for the life of the tab.
|
|
1788
|
+
void this.deps.hooks.resumeSyncLeaderDuties().catch((e) => {
|
|
1789
|
+
<span class="cstat-no" title="statement not covered" > this.deps.logger.error(</span>
|
|
1790
|
+
<span class="cstat-no" title="statement not covered" > { err: e, Category: 'sp00ky-client::TabsCoordinator' },</span>
|
|
1791
|
+
<span class="cstat-no" title="statement not covered" > 'Leader duties failed after promotion'</span>
|
|
1792
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
1793
|
+
});
|
|
1794
|
+
} catch (e) {
|
|
1795
|
+
const reason = e instanceof Error ? e.<span class="branch-0 cbranch-no" title="branch not covered" >message : String(e);</span>
|
|
1796
|
+
this.deps.logger.error(
|
|
1797
|
+
{ err: e, Category: 'sp00ky-client::TabsCoordinator' },
|
|
1798
|
+
'Promotion failed'
|
|
1799
|
+
);
|
|
1800
|
+
this.<span class="branch-0 cbranch-no" title="branch not covered" >hub?.detachAll()</span>;
|
|
1801
|
+
this.hub = null;
|
|
1802
|
+
// Give back everything this attempt claimed. The broker does NOT demote a
|
|
1803
|
+
// tab whose promotion failed (leader-failed clears leadership without a
|
|
1804
|
+
// demote), so nothing else ever frees these. The tab lock name is shared
|
|
1805
|
+
// per namespace, so keeping it after failing to lead makes EVERY later
|
|
1806
|
+
// election in this namespace fail with 'leader tab lock unavailable' —
|
|
1807
|
+
// one OPFS-busy promotion would wedge the whole app into solo mode.
|
|
1808
|
+
if (previousRole === 'leader') <span class="branch-0 cbranch-no" title="branch not covered" >await this.deps.hooks.releaseOwnership();</span>
|
|
1809
|
+
this.tabLock?.release();
|
|
1810
|
+
this.tabLock = null;
|
|
1811
|
+
this.broker.send({
|
|
1812
|
+
type: 'leader-failed',
|
|
1813
|
+
tabId: this.deps.tabId,
|
|
1814
|
+
bucketId: this.bucketId,
|
|
1815
|
+
leadershipId: msg.leadershipId,
|
|
1816
|
+
reason,
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
private async demote(leadershipId: number): Promise<void> {
|
|
1822
|
+
if (this.role !== 'leader' || leadershipId !== this.leadershipId) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
|
|
1823
|
+
await this.teardownLeader();
|
|
1824
|
+
this.deps.hooks.onLeaderLost('demoted');
|
|
1825
|
+
// Stay roleless; the broker sends use-follower-ports (or become-leader)
|
|
1826
|
+
// for whatever comes next.
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
private tabLock: LeaderLockHandle | null = null;
|
|
1830
|
+
|
|
1831
|
+
private async teardownLeader(): Promise<void> {
|
|
1832
|
+
this.hub?.detachAll();
|
|
1833
|
+
this.hub = null;
|
|
1834
|
+
await this.deps.hooks.releaseOwnership();
|
|
1835
|
+
this.tabLock?.release();
|
|
1836
|
+
this.tabLock = null;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
private teardownFollower(reason: string): void {
|
|
1840
|
+
this.forwarder?.unbind();
|
|
1841
|
+
this.deps.hooks.onLeaderLost(reason);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
private async serveFollower(
|
|
1845
|
+
followerTabId: TabId,
|
|
1846
|
+
leadershipId: number,
|
|
1847
|
+
dbPort: MessagePort,
|
|
1848
|
+
syncPort: MessagePort
|
|
1849
|
+
): Promise<void> {
|
|
1850
|
+
if (this.role !== 'leader' || leadershipId !== this.leadershipId || !this.hub) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
|
|
1851
|
+
<span class="cstat-no" title="statement not covered" > dbPort.close();</span>
|
|
1852
|
+
<span class="cstat-no" title="statement not covered" > syncPort.close();</span>
|
|
1853
|
+
<span class="cstat-no" title="statement not covered" > return;</span>
|
|
1854
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1855
|
+
await this.deps.hooks.exposeClientPort(followerTabId, dbPort);
|
|
1856
|
+
this.hub.attach(followerTabId, syncPort);
|
|
1857
|
+
this.hub.sendTo(followerTabId, {
|
|
1858
|
+
type: 'db-ready',
|
|
1859
|
+
leadershipId,
|
|
1860
|
+
bucketId: this.bucketId,
|
|
1861
|
+
storageHealth: this.deps.hooks.currentStorageHealth(),
|
|
1862
|
+
});
|
|
1863
|
+
this.broker.send({
|
|
1864
|
+
type: 'follower-port-attached',
|
|
1865
|
+
tabId: this.deps.tabId,
|
|
1866
|
+
bucketId: this.bucketId,
|
|
1867
|
+
leadershipId,
|
|
1868
|
+
followerTabId,
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
private async attachToLeader(
|
|
1873
|
+
leaderTabId: TabId,
|
|
1874
|
+
leadershipId: number,
|
|
1875
|
+
dbPort: MessagePort,
|
|
1876
|
+
syncPort: MessagePort
|
|
1877
|
+
): Promise<void> {
|
|
1878
|
+
if (this.closed || this.role === 'leader') <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
|
|
1879
|
+
<span class="cstat-no" title="statement not covered" > dbPort.close();</span>
|
|
1880
|
+
<span class="cstat-no" title="statement not covered" > syncPort.close();</span>
|
|
1881
|
+
<span class="cstat-no" title="statement not covered" > return;</span>
|
|
1882
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1883
|
+
this.leaderTabId = leaderTabId;
|
|
1884
|
+
this.leadershipId = leadershipId;
|
|
1885
|
+
if (!this.forwarder) this.forwarder = new SyncForwarder(this.deps.tabId);
|
|
1886
|
+
// db-ready arrives on the syncPort and carries the snapshot the engine
|
|
1887
|
+
// needs; bind sync first, adopt the store on receipt.
|
|
1888
|
+
const forwarder = this.forwarder;
|
|
1889
|
+
await new Promise<void>((resolve) => {
|
|
1890
|
+
let adopted = false;
|
|
1891
|
+
let attached = false;
|
|
1892
|
+
// Relay traffic that lands between `db-ready` and the sync hooks being
|
|
1893
|
+
// installed (the store adopt is async). Held and replayed, not dropped:
|
|
1894
|
+
// on a first attach there is no previous handler, so an ingest-relay or
|
|
1895
|
+
// settled notice in that window used to vanish.
|
|
1896
|
+
const pending: LeaderToFollowerMessage[] = [];
|
|
1897
|
+
const previousHandler = forwarder.onLeaderMessage;
|
|
1898
|
+
forwarder.onLeaderMessage = (msg) => {
|
|
1899
|
+
if (msg.type === 'db-ready' && !adopted) {
|
|
1900
|
+
adopted = true;
|
|
1901
|
+
void this.deps.hooks
|
|
1902
|
+
.adoptAttached(dbPort, {
|
|
1903
|
+
bucketId: msg.bucketId,
|
|
1904
|
+
storageHealth: msg.storageHealth,
|
|
1905
|
+
leadershipId: msg.leadershipId,
|
|
1906
|
+
})
|
|
1907
|
+
.then(() => {
|
|
1908
|
+
this.deps.hooks.becomeSyncFollower(forwarder);
|
|
1909
|
+
attached = true;
|
|
1910
|
+
const backlog = pending.splice(0);
|
|
1911
|
+
for (const m of backlog) forwarder.onLeaderMessage?.(m);
|
|
1912
|
+
this.setRole('follower');
|
|
1913
|
+
resolve();
|
|
1914
|
+
});
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
if (!attached) {
|
|
1918
|
+
pending.push(msg);
|
|
1919
|
+
return;
|
|
1920
|
+
<span class="branch-0 cbranch-no" title="branch not covered" > }</span>
|
|
1921
|
+
<span class="cstat-no" title="statement not covered" > previousHandler?.(msg);</span>
|
|
1922
|
+
};
|
|
1923
|
+
forwarder.rebind(syncPort);
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
private async handleLeaderGone(leadershipId: number): Promise<void> {
|
|
1928
|
+
if (this.role === 'leader') return;
|
|
1929
|
+
if (leadershipId < this.leadershipId) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
|
|
1930
|
+
// Detached limbo: the engine parks ops via onLeaderLost and the role stays
|
|
1931
|
+
// 'follower' (this tab is still in shared mode, just between leaders). The
|
|
1932
|
+
// next use-follower-ports or become-leader resolves it either way.
|
|
1933
|
+
this.teardownFollower('leader gone');
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
private <span class="fstat-no" title="function not covered" >fallbackToSolo(reason: string): void {</span>
|
|
1937
|
+
<span class="cstat-no" title="statement not covered" > this.deps.logger.warn(</span>
|
|
1938
|
+
<span class="cstat-no" title="statement not covered" > { reason, Category: 'sp00ky-client::TabsCoordinator' },</span>
|
|
1939
|
+
<span class="cstat-no" title="statement not covered" > 'Shared-tabs unavailable; running solo'</span>
|
|
1940
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
1941
|
+
<span class="cstat-no" title="statement not covered" > this.deps.hooks.becomeSyncSolo();</span>
|
|
1942
|
+
<span class="cstat-no" title="statement not covered" > this.setRole('solo');</span>
|
|
1943
|
+
// start() treats solo as a rejection so the caller boots the plain path.
|
|
1944
|
+
<span class="cstat-no" title="statement not covered" > this.startReject?.(new Error(`shared-tabs unavailable: ${reason}`));</span>
|
|
1945
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1946
|
+
|
|
1947
|
+
<span class="fstat-no" title="function not covered" > get syncHub(): LeaderSyncHub | null {</span>
|
|
1948
|
+
<span class="cstat-no" title="statement not covered" > return this.hub;</span>
|
|
1949
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1950
|
+
<span class="fstat-no" title="function not covered" > get syncForwarder(): SyncForwarder | null {</span>
|
|
1951
|
+
<span class="cstat-no" title="statement not covered" > return this.forwarder;</span>
|
|
1952
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1953
|
+
<span class="fstat-no" title="function not covered" > get tabId(): TabId {</span>
|
|
1954
|
+
<span class="cstat-no" title="statement not covered" > return this.deps.tabId;</span>
|
|
1955
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1956
|
+
|
|
1957
|
+
async stop(): Promise<void> {
|
|
1958
|
+
this.closed = true;
|
|
1959
|
+
if (this.role === 'leader') await this.teardownLeader();
|
|
1960
|
+
else this.forwarder?.unbind();
|
|
1961
|
+
this.broker.send({ type: 'shutdown', tabId: this.deps.tabId, bucketId: this.bucketId });
|
|
1962
|
+
this.broker.close();
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
</pre></td></tr></table></pre>
|
|
1966
|
+
|
|
1967
|
+
<div class='push'></div><!-- for sticky footer -->
|
|
1968
|
+
</div><!-- /wrapper -->
|
|
1969
|
+
<div class='footer quiet pad2 space-top1 center small'>
|
|
1970
|
+
Code coverage generated by
|
|
1971
|
+
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
1972
|
+
at 2026-09-09T09:49:51.780Z
|
|
1973
|
+
</div>
|
|
1974
|
+
<script src="../../../prettify.js"></script>
|
|
1975
|
+
<script>
|
|
1976
|
+
window.onload = function () {
|
|
1977
|
+
prettyPrint();
|
|
1978
|
+
};
|
|
1979
|
+
</script>
|
|
1980
|
+
<script src="../../../sorter.js"></script>
|
|
1981
|
+
<script src="../../../block-navigation.js"></script>
|
|
1982
|
+
</body>
|
|
1983
|
+
</html>
|
|
1984
|
+
|