@rocicorp/zero 1.12.0-canary.12 → 1.12.0-canary.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/out/replicache/src/btree/diff.d.ts.map +1 -1
  2. package/out/replicache/src/btree/diff.js +1 -2
  3. package/out/replicache/src/btree/diff.js.map +1 -1
  4. package/out/replicache/src/btree/read.d.ts +31 -1
  5. package/out/replicache/src/btree/read.d.ts.map +1 -1
  6. package/out/replicache/src/btree/read.js +74 -19
  7. package/out/replicache/src/btree/read.js.map +1 -1
  8. package/out/replicache/src/btree/splice.d.ts.map +1 -1
  9. package/out/replicache/src/btree/splice.js +2 -1
  10. package/out/replicache/src/btree/splice.js.map +1 -1
  11. package/out/replicache/src/btree/write.d.ts.map +1 -1
  12. package/out/replicache/src/btree/write.js +2 -1
  13. package/out/replicache/src/btree/write.js.map +1 -1
  14. package/out/replicache/src/persist/collect-idb-databases.d.ts +1 -0
  15. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  16. package/out/replicache/src/persist/collect-idb-databases.js +13 -5
  17. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  18. package/out/replicache/src/scan-iterator.js +1 -1
  19. package/out/replicache/src/sync/request-id.js +2 -2
  20. package/out/replicache/src/sync/request-id.js.map +1 -1
  21. package/out/shared/src/bigint-json.d.ts.map +1 -1
  22. package/out/shared/src/bigint-json.js +1 -0
  23. package/out/shared/src/bigint-json.js.map +1 -1
  24. package/out/shared/src/btree-set.d.ts +21 -0
  25. package/out/shared/src/btree-set.d.ts.map +1 -1
  26. package/out/shared/src/btree-set.js +54 -0
  27. package/out/shared/src/btree-set.js.map +1 -1
  28. package/out/shared/src/headers.js +20 -0
  29. package/out/shared/src/headers.js.map +1 -0
  30. package/out/shared/src/natural-runs.d.ts +191 -0
  31. package/out/shared/src/natural-runs.d.ts.map +1 -0
  32. package/out/shared/src/natural-runs.js +337 -0
  33. package/out/shared/src/natural-runs.js.map +1 -0
  34. package/out/shared/src/random-uint64.d.ts.map +1 -1
  35. package/out/shared/src/random-uint64.js +2 -2
  36. package/out/shared/src/random-uint64.js.map +1 -1
  37. package/out/shared/src/random-values.d.ts +19 -1
  38. package/out/shared/src/random-values.d.ts.map +1 -1
  39. package/out/shared/src/random-values.js +38 -4
  40. package/out/shared/src/random-values.js.map +1 -1
  41. package/out/shared/src/tuple-map.d.ts +22 -0
  42. package/out/shared/src/tuple-map.d.ts.map +1 -0
  43. package/out/shared/src/tuple-map.js +48 -0
  44. package/out/shared/src/tuple-map.js.map +1 -0
  45. package/out/zero/package.js +1 -1
  46. package/out/zero/package.js.map +1 -1
  47. package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
  48. package/out/zero-cache/src/auth/auth.js +5 -0
  49. package/out/zero-cache/src/auth/auth.js.map +1 -1
  50. package/out/zero-cache/src/config/zero-config.d.ts +21 -0
  51. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  52. package/out/zero-cache/src/config/zero-config.js +47 -9
  53. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  54. package/out/zero-cache/src/custom-queries/transform-query.js +2 -2
  55. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  56. package/out/zero-cache/src/db/pg-to-lite.d.ts.map +1 -1
  57. package/out/zero-cache/src/db/pg-to-lite.js +3 -2
  58. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  59. package/out/zero-cache/src/db/pg-type-parser.js +55 -2
  60. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  61. package/out/zero-cache/src/server/change-streamer.js +5 -2
  62. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  63. package/out/zero-cache/src/server/inspector-delegate.js +6 -29
  64. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  65. package/out/zero-cache/src/server/main.js +5 -0
  66. package/out/zero-cache/src/server/main.js.map +1 -1
  67. package/out/zero-cache/src/server/replicator.js +3 -2
  68. package/out/zero-cache/src/server/replicator.js.map +1 -1
  69. package/out/zero-cache/src/server/syncer.js +5 -4
  70. package/out/zero-cache/src/server/syncer.js.map +1 -1
  71. package/out/zero-cache/src/server/worker-dispatcher.d.ts.map +1 -1
  72. package/out/zero-cache/src/server/worker-dispatcher.js +2 -1
  73. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  74. package/out/zero-cache/src/services/admin-auth.d.ts +22 -0
  75. package/out/zero-cache/src/services/admin-auth.d.ts.map +1 -0
  76. package/out/zero-cache/src/services/admin-auth.js +42 -0
  77. package/out/zero-cache/src/services/admin-auth.js.map +1 -0
  78. package/out/zero-cache/src/services/auth-attempt-counter.d.ts +33 -0
  79. package/out/zero-cache/src/services/auth-attempt-counter.d.ts.map +1 -0
  80. package/out/zero-cache/src/services/auth-attempt-counter.js +61 -0
  81. package/out/zero-cache/src/services/auth-attempt-counter.js.map +1 -0
  82. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +1 -1
  83. package/out/zero-cache/src/services/change-source/pg/change-source.js +110 -13
  84. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  85. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +13 -7
  86. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  87. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +38 -5
  88. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  89. package/out/zero-cache/src/services/change-source/pg/schema/init.js +10 -1
  90. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  91. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +4 -0
  92. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  93. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +10 -3
  94. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  95. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +23 -2
  96. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  97. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +2 -2
  98. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  99. package/out/zero-cache/src/services/change-streamer/change-streamer.js +10 -3
  100. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  101. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  102. package/out/zero-cache/src/services/heapz.d.ts.map +1 -1
  103. package/out/zero-cache/src/services/heapz.js +4 -5
  104. package/out/zero-cache/src/services/heapz.js.map +1 -1
  105. package/out/zero-cache/src/services/mutagen/error.d.ts +5 -0
  106. package/out/zero-cache/src/services/mutagen/error.d.ts.map +1 -1
  107. package/out/zero-cache/src/services/mutagen/error.js +7 -1
  108. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  109. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  110. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  111. package/out/zero-cache/src/services/mutagen/pusher.js +1 -1
  112. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  113. package/out/zero-cache/src/services/plannerz.d.ts.map +1 -1
  114. package/out/zero-cache/src/services/plannerz.js +3 -7
  115. package/out/zero-cache/src/services/plannerz.js.map +1 -1
  116. package/out/zero-cache/src/services/profz.d.ts.map +1 -1
  117. package/out/zero-cache/src/services/profz.js +4 -13
  118. package/out/zero-cache/src/services/profz.js.map +1 -1
  119. package/out/zero-cache/src/services/replicator/change-processor.js +29 -2
  120. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  121. package/out/zero-cache/src/services/replicator/incremental-sync.js +3 -3
  122. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  123. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  124. package/out/zero-cache/src/services/statz.js +2 -7
  125. package/out/zero-cache/src/services/statz.js.map +1 -1
  126. package/out/zero-cache/src/services/view-syncer/client-handler.js +22 -1
  127. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  128. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +40 -2
  129. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  130. package/out/zero-cache/src/services/view-syncer/cvr-store.js +54 -17
  131. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  132. package/out/zero-cache/src/services/view-syncer/cvr.js +11 -0
  133. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  134. package/out/zero-cache/src/services/view-syncer/inspect-auth-limiter.js +107 -0
  135. package/out/zero-cache/src/services/view-syncer/inspect-auth-limiter.js.map +1 -0
  136. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +11 -6
  137. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  138. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +2 -1
  139. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  140. package/out/zero-cache/src/services/view-syncer/row-record-cache.js +16 -14
  141. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  142. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +4 -1
  143. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  144. package/out/zero-cache/src/services/view-syncer/schema/init.js +3 -0
  145. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  146. package/out/zero-cache/src/services/view-syncer/view-syncer.js +89 -51
  147. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  148. package/out/zero-cache/src/types/client-group-log-id.d.ts +9 -0
  149. package/out/zero-cache/src/types/client-group-log-id.d.ts.map +1 -0
  150. package/out/zero-cache/src/types/client-group-log-id.js +16 -0
  151. package/out/zero-cache/src/types/client-group-log-id.js.map +1 -0
  152. package/out/zero-cache/src/types/lite.d.ts.map +1 -1
  153. package/out/zero-cache/src/types/lite.js +1 -0
  154. package/out/zero-cache/src/types/lite.js.map +1 -1
  155. package/out/zero-cache/src/types/pg.d.ts +13 -2
  156. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  157. package/out/zero-cache/src/types/pg.js +2 -2
  158. package/out/zero-cache/src/types/pg.js.map +1 -1
  159. package/out/zero-cache/src/types/poke-chunk.d.ts +25 -4
  160. package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -1
  161. package/out/zero-cache/src/types/poke-chunk.js +46 -5
  162. package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
  163. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  164. package/out/zero-cache/src/workers/connection.js +10 -3
  165. package/out/zero-cache/src/workers/connection.js.map +1 -1
  166. package/out/zero-cache/src/workers/rehome.d.ts +10 -0
  167. package/out/zero-cache/src/workers/rehome.d.ts.map +1 -1
  168. package/out/zero-cache/src/workers/rehome.js +4 -2
  169. package/out/zero-cache/src/workers/rehome.js.map +1 -1
  170. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +3 -2
  171. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  172. package/out/zero-cache/src/workers/syncer.js +15 -15
  173. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  174. package/out/zero-client/src/client/inspector/inspector.d.ts +4 -0
  175. package/out/zero-client/src/client/inspector/inspector.d.ts.map +1 -1
  176. package/out/zero-client/src/client/inspector/inspector.js +4 -0
  177. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  178. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts +10 -2
  179. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
  180. package/out/zero-client/src/client/inspector/lazy-inspector.js +38 -31
  181. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  182. package/out/zero-client/src/client/inspector/query.d.ts +2 -2
  183. package/out/zero-client/src/client/inspector/query.d.ts.map +1 -1
  184. package/out/zero-client/src/client/inspector/query.js +4 -4
  185. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  186. package/out/zero-client/src/client/ivm-branch.d.ts +3 -0
  187. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  188. package/out/zero-client/src/client/ivm-branch.js +10 -5
  189. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  190. package/out/zero-client/src/client/reconnect-backoff.d.ts +31 -0
  191. package/out/zero-client/src/client/reconnect-backoff.d.ts.map +1 -0
  192. package/out/zero-client/src/client/reconnect-backoff.js +44 -0
  193. package/out/zero-client/src/client/reconnect-backoff.js.map +1 -0
  194. package/out/zero-client/src/client/version.js +1 -1
  195. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  196. package/out/zero-client/src/client/zero.js +7 -0
  197. package/out/zero-client/src/client/zero.js.map +1 -1
  198. package/out/zero-client/src/util/nanoid.js +2 -2
  199. package/out/zero-client/src/util/nanoid.js.map +1 -1
  200. package/out/zero-protocol/src/up.d.ts +42 -42
  201. package/out/zero-server/src/process-mutations.d.ts +10 -0
  202. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  203. package/out/zero-server/src/process-mutations.js +39 -6
  204. package/out/zero-server/src/process-mutations.js.map +1 -1
  205. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  206. package/out/zql/src/ivm/memory-source.js +89 -6
  207. package/out/zql/src/ivm/memory-source.js.map +1 -1
  208. package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
  209. package/out/zql/src/planner/planner-connection.js +37 -3
  210. package/out/zql/src/planner/planner-connection.js.map +1 -1
  211. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"collect-idb-databases.js","names":[],"sources":["../../../../../replicache/src/persist/collect-idb-databases.ts"],"sourcesContent":["import type {LogContext, LogLevel, LogSink} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport {InvalidRefCountError} from '../dag/invalid-ref-count-error.ts';\nimport {StoreImpl} from '../dag/store-impl.ts';\nimport type {Store} from '../dag/store.ts';\nimport {\n addDeletedClients,\n getDeletedClients,\n mergeDeletedClients,\n normalizeDeletedClients,\n type DeletedClients,\n type WritableDeletedClients,\n} from '../deleted-clients.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport {getKVStoreProvider} from '../get-kv-store-provider.ts';\nimport {assertHash, newRandomHash} from '../hash.ts';\nimport {\n fallbackStoreProvider,\n isFallbackStore,\n} from '../kv/mem-fallback-store.ts';\nimport type {CreateStore, DropStore, StoreProvider} from '../kv/store.ts';\nimport {createLogContext} from '../log-options.ts';\nimport type {StorageFailureError} from '../storage-failure.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n} from './client-groups.ts';\nimport type {OnClientsDeleted} from './clients.ts';\nimport {getClients} from './clients.ts';\nimport {getIDBDatabasesDBName} from './idb-databases-store-db-name.ts';\nimport type {IndexedDBDatabase} from './idb-databases-store.ts';\nimport {IDBDatabasesStore} from './idb-databases-store.ts';\n\n/**\n * How frequently to try to collect\n */\nexport const COLLECT_IDB_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * We delay the initial collection to prevent doing it at startup.\n */\nexport const INITIAL_COLLECT_IDB_DELAY = 5 * 60 * 1000; // 5 minutes\n\nexport function initCollectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n kvStoreProvider: StoreProvider,\n collectInterval: number,\n initialCollectDelay: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n lc: LogContext,\n signal: AbortSignal,\n newDagStore: NewDagStore = defaultNewDagStore,\n onStorageFailure?: ((failure: StorageFailureError) => void) | undefined,\n): void {\n let initial = true;\n initBgIntervalProcess(\n 'CollectIDBDatabases',\n async () => {\n await collectIDBDatabases(\n idbDatabasesStore,\n Date.now(),\n maxAge,\n kvStoreProvider,\n enableMutationRecovery,\n onClientsDeleted,\n newDagStore,\n );\n },\n () => {\n if (initial) {\n initial = false;\n return initialCollectDelay;\n }\n return collectInterval;\n },\n lc,\n signal,\n onStorageFailure,\n );\n}\n\n/**\n * Collects IDB databases that are no longer needed.\n */\nexport async function collectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n now: number,\n maxAge: number,\n kvStoreProvider: StoreProvider,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n newDagStore: NewDagStore = defaultNewDagStore,\n): Promise<void> {\n const databases = await idbDatabasesStore.getDatabases();\n\n const dbs = Object.values(databases);\n const collectResults = await Promise.all(\n dbs.map(\n async db =>\n [\n db.name,\n await gatherDatabaseInfoForCollect(\n db,\n now,\n maxAge,\n enableMutationRecovery,\n kvStoreProvider.create,\n newDagStore,\n ),\n ] as const,\n ),\n );\n\n const dbNamesToRemove: string[] = [];\n const dbNamesToKeep: string[] = [];\n const deletedClientsToRemove: WritableDeletedClients = [];\n for (const [dbName, [canCollect, deletedClients]] of collectResults) {\n if (canCollect) {\n dbNamesToRemove.push(dbName);\n deletedClientsToRemove.push(...deletedClients);\n } else {\n dbNamesToKeep.push(dbName);\n }\n }\n\n const {errors} = await dropDatabases(\n idbDatabasesStore,\n dbNamesToRemove,\n kvStoreProvider.drop,\n );\n if (errors.length) {\n throw errors[0];\n }\n\n if (deletedClientsToRemove.length > 0) {\n // Add the deleted clients to all the dbs that survived the collection.\n let newDeletedClients: DeletedClients = deletedClientsToRemove;\n for (const name of dbNamesToKeep) {\n let dbDeletedClients: DeletedClients;\n try {\n dbDeletedClients = await withWrite(\n newDagStore(name, kvStoreProvider.create),\n dagWrite => addDeletedClients(dagWrite, deletedClientsToRemove),\n );\n } catch (e) {\n if (!(e instanceof InvalidRefCountError)) {\n throw e;\n }\n // This database is corrupt. Its own instance will find out and\n // recover the next time it writes to it; here, just skip it and keep\n // collecting into the others, instead of letting one corrupt\n // database break every collection run forever.\n continue;\n }\n newDeletedClients = mergeDeletedClients(\n newDeletedClients,\n dbDeletedClients,\n );\n }\n // normalize and dedupe\n const normalizedDeletedClients = normalizeDeletedClients(newDeletedClients);\n\n // Call the callback with the normalized deleted clients\n await onClientsDeleted(normalizedDeletedClients);\n }\n}\n\nexport async function dropDatabaseInternal(\n name: string,\n idbDatabasesStore: IDBDatabasesStore,\n kvDropStore: DropStore,\n) {\n await kvDropStore(name);\n await idbDatabasesStore.deleteDatabases([name]);\n}\n\nasync function dropDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n namesToRemove: string[],\n kvDropStore: DropStore,\n): Promise<{dropped: string[]; errors: unknown[]}> {\n // Try to remove the databases in parallel. Don't let a single reject fail the\n // other ones. We will check for failures afterwards.\n const dropStoreResults = await Promise.allSettled(\n namesToRemove.map(async name => {\n await dropDatabaseInternal(name, idbDatabasesStore, kvDropStore);\n return name;\n }),\n );\n\n const dropped: string[] = [];\n const errors: unknown[] = [];\n for (const result of dropStoreResults) {\n if (result.status === 'fulfilled') {\n dropped.push(result.value);\n } else {\n errors.push(result.reason);\n }\n }\n\n return {dropped, errors};\n}\n\n/**\n * Creates the dag store used to inspect and update a database during\n * collection. Callers can override it to install store hooks, for example so\n * a corrupt ref count found while writing deleted clients is recovered from.\n */\nexport type NewDagStore = (name: string, kvCreateStore: CreateStore) => Store;\n\nfunction defaultNewDagStore(name: string, kvCreateStore: CreateStore): Store {\n const perKvStore = kvCreateStore(name);\n return new StoreImpl(perKvStore, newRandomHash, assertHash);\n}\n\n/**\n * If any client has a recent heartbeat or there are pending mutations we\n * return `[false]`. Otherwise we return `[true, deletedClients]`.\n */\nfunction gatherDatabaseInfoForCollect(\n db: IndexedDBDatabase,\n now: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n kvCreateStore: CreateStore,\n newDagStore: NewDagStore,\n): MaybePromise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n if (db.replicacheFormatVersion > FormatVersion.Latest) {\n return [false];\n }\n\n // If increase the format version we need to decide how to deal with this\n // logic.\n assert(\n db.replicacheFormatVersion === FormatVersion.DD31 ||\n db.replicacheFormatVersion === FormatVersion.V6 ||\n db.replicacheFormatVersion === FormatVersion.V7,\n () =>\n `Expected replicacheFormatVersion to be DD31, V6, or V7, got ${db.replicacheFormatVersion}`,\n );\n return canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery,\n newDagStore(db.name, kvCreateStore),\n now,\n maxAge,\n );\n}\n\n/**\n * Options for `dropDatabase` and `dropAllDatabases`.\n */\nexport type DropDatabaseOptions = {\n /**\n * Allows providing a custom implementation of the underlying storage layer.\n * Default is `'idb'`.\n */\n kvStore?: 'idb' | 'mem' | StoreProvider | undefined;\n /**\n * Determines how much logging to do. When this is set to `'debug'`,\n * Replicache will also log `'info'` and `'error'` messages. When set to\n * `'info'` we log `'info'` and `'error'` but not `'debug'`. When set to\n * `'error'` we only log `'error'` messages.\n * Default is `'info'`.\n */\n logLevel?: LogLevel | undefined;\n /**\n * Enables custom handling of logs.\n *\n * By default logs are logged to the console. If you would like logs to be\n * sent elsewhere (e.g. to a cloud logging service like DataDog) you can\n * provide an array of {@link LogSink}s. Logs at or above\n * {@link DropDatabaseOptions.logLevel} are sent to each of these {@link LogSink}s.\n * If you would still like logs to go to the console, include\n * `consoleLogSink` in the array.\n *\n * ```ts\n * logSinks: [consoleLogSink, myCloudLogSink],\n * ```\n * Default is `[consoleLogSink]`.\n */\n logSinks?: LogSink[] | undefined;\n};\n\n/**\n * Drops the specified database, from memory as well as from the configured\n * store (see `onStorageFailure`). Rejects if the configured store cannot be\n * opened or dropped.\n * @param dbName The name of the database to drop.\n * @param opts Options for dropping the database.\n */\nexport async function dropDatabase(dbName: string, opts?: DropDatabaseOptions) {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropDatabase: undefined,\n });\n for (const {provider, drops} of dropTargets(logContext, opts)) {\n if (!drops(dbName)) {\n continue;\n }\n const store = new IDBDatabasesStore(provider.create);\n try {\n await dropDatabaseInternal(dbName, store, provider.drop);\n } finally {\n await closeIgnoringErrors(store);\n }\n }\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache, and the in-memory\n * databases instances ran on after a storage failure (see\n * `onStorageFailure`).\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping. Rejects if the configured store\n * cannot be opened.\n */\nexport function dropAllDatabases(opts?: DropDatabaseOptions): Promise<{\n dropped: string[];\n errors: unknown[];\n}> {\n return dropMatchingDatabases(() => true, opts);\n}\n\n/**\n * Like {@link dropAllDatabases}, for the databases whose registry record\n * matches `predicate`.\n */\nexport async function dropMatchingDatabases(\n predicate: (db: IndexedDBDatabase) => boolean,\n opts?: DropDatabaseOptions,\n): Promise<{\n dropped: string[];\n errors: unknown[];\n}> {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropDatabases: undefined,\n });\n const dropped = new Set<string>();\n const errors: unknown[] = [];\n for (const {provider, drops} of dropTargets(logContext, opts)) {\n const store = new IDBDatabasesStore(provider.create);\n try {\n const databases = await store.getDatabases();\n const dbNames = Object.values(databases)\n .filter(db => predicate(db) && drops(db.name))\n .map(db => db.name);\n const result = await dropDatabases(store, dbNames, provider.drop);\n result.dropped.forEach(name => dropped.add(name));\n errors.push(...result.errors);\n } finally {\n await closeIgnoringErrors(store);\n }\n }\n return {dropped: [...dropped], errors};\n}\n\ntype DropTarget = {\n provider: StoreProvider;\n /** Whether a database of this name is dropped from `provider`. */\n drops: (name: string) => boolean;\n};\n\n/**\n * The memory stores instances fell back to first, then the configured store.\n * An instance whose store failed to open runs on memory stores of the same\n * names (see `onStorageFailure`), and memory stores outlive the instance for\n * the life of the process, so a drop clears them whatever the configured\n * store is. Only those: the memory stores of `kvStore: 'mem'` instances are\n * left alone unless `'mem'` is the store being dropped from. The configured\n * store is still dropped, and its failure is reported: it may hold data on\n * disk.\n */\nfunction dropTargets(\n lc: LogContext,\n opts: DropDatabaseOptions | undefined,\n): DropTarget[] {\n const all = () => true;\n if (opts?.kvStore === 'mem') {\n return [{provider: getKVStoreProvider(lc, 'mem'), drops: all}];\n }\n const targets: DropTarget[] = [];\n if (isFallbackStore(getIDBDatabasesDBName())) {\n targets.push({provider: fallbackStoreProvider, drops: isFallbackStore});\n }\n targets.push({provider: getKVStoreProvider(lc, opts?.kvStore), drops: all});\n return targets;\n}\n\n/**\n * A store that failed to open may also fail to close; the drop's own result\n * is what the caller needs.\n */\nfunction closeIgnoringErrors(store: IDBDatabasesStore): Promise<void> {\n return store.close().catch(() => undefined);\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n *\n * @deprecated Use `dropAllDatabases` instead.\n */\nexport function deleteAllReplicacheData(opts?: DropDatabaseOptions) {\n return dropAllDatabases(opts);\n}\n\n/**\n * If there are pending mutations in any of the clients in this db we return\n * `[false]`. If any client has a recent heartbeat we also return `[false]`.\n * Otherwise we return `[true, deletedClients]`.\n */\nfunction canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery: boolean,\n perdag: Store,\n now: number,\n maxAge: number,\n): Promise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n return withRead(perdag, async read => {\n // If mutation recovery is disabled we do not care if there are pending\n // mutations when we decide if we can collect the database.\n if (enableMutationRecovery) {\n const clientGroups = await getClientGroups(read);\n for (const clientGroup of clientGroups.values()) {\n if (clientGroupHasPendingMutations(clientGroup)) {\n return [false];\n }\n }\n }\n\n const clients = await getClients(read);\n\n // Don't collect if any client has a recent heartbeat (is still active).\n // This is defense in depth - normally lastOpenedTimestampMS is kept fresh\n // by the heartbeat, but this check protects against edge cases.\n for (const [, client] of clients) {\n if (now - client.heartbeatTimestampMs < maxAge) {\n return [false];\n }\n }\n\n const existingDeletedClients = await getDeletedClients(read);\n const deletedClients: WritableDeletedClients = [...existingDeletedClients];\n\n // Add all current clients to the deleted clients list\n for (const [clientID, client] of clients) {\n deletedClients.push({\n clientID,\n clientGroupID: client.clientGroupID,\n });\n }\n\n // The normalization (deduping and sorting) will be done when storing\n return [true, deletedClients];\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"collect-idb-databases.js","names":[],"sources":["../../../../../replicache/src/persist/collect-idb-databases.ts"],"sourcesContent":["import type {LogContext, LogLevel, LogSink} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport {InvalidRefCountError} from '../dag/invalid-ref-count-error.ts';\nimport {StoreImpl} from '../dag/store-impl.ts';\nimport type {Store} from '../dag/store.ts';\nimport {\n addDeletedClients,\n getDeletedClients,\n mergeDeletedClients,\n normalizeDeletedClients,\n type DeletedClients,\n type WritableDeletedClients,\n} from '../deleted-clients.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport {getKVStoreProvider} from '../get-kv-store-provider.ts';\nimport {assertHash, newRandomHash} from '../hash.ts';\nimport {\n fallbackStoreProvider,\n isFallbackStore,\n} from '../kv/mem-fallback-store.ts';\nimport type {CreateStore, DropStore, StoreProvider} from '../kv/store.ts';\nimport {createLogContext} from '../log-options.ts';\nimport type {StorageFailureError} from '../storage-failure.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n} from './client-groups.ts';\nimport type {OnClientsDeleted} from './clients.ts';\nimport {getClients} from './clients.ts';\nimport {getIDBDatabasesDBName} from './idb-databases-store-db-name.ts';\nimport type {IndexedDBDatabase} from './idb-databases-store.ts';\nimport {IDBDatabasesStore} from './idb-databases-store.ts';\n\n/**\n * How frequently to try to collect\n */\nexport const COLLECT_IDB_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * We delay the initial collection to prevent doing it at startup.\n */\nexport const INITIAL_COLLECT_IDB_DELAY = 5 * 60 * 1000; // 5 minutes\n\nexport function initCollectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n kvStoreProvider: StoreProvider,\n collectInterval: number,\n initialCollectDelay: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n lc: LogContext,\n signal: AbortSignal,\n newDagStore: NewDagStore = defaultNewDagStore,\n onStorageFailure?: ((failure: StorageFailureError) => void) | undefined,\n): void {\n let initial = true;\n initBgIntervalProcess(\n 'CollectIDBDatabases',\n async () => {\n await collectIDBDatabases(\n idbDatabasesStore,\n Date.now(),\n maxAge,\n kvStoreProvider,\n enableMutationRecovery,\n onClientsDeleted,\n newDagStore,\n );\n },\n () => {\n if (initial) {\n initial = false;\n return initialCollectDelay;\n }\n return collectInterval;\n },\n lc,\n signal,\n onStorageFailure,\n );\n}\n\n/**\n * Collects IDB databases that are no longer needed.\n */\nexport async function collectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n now: number,\n maxAge: number,\n kvStoreProvider: StoreProvider,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n newDagStore: NewDagStore = defaultNewDagStore,\n): Promise<void> {\n const databases = await idbDatabasesStore.getDatabases();\n\n const dbs = Object.values(databases);\n const collectResults = await Promise.all(\n dbs.map(\n async db =>\n [\n db.name,\n await gatherDatabaseInfoForCollect(\n db,\n now,\n maxAge,\n enableMutationRecovery,\n kvStoreProvider.create,\n newDagStore,\n ),\n ] as const,\n ),\n );\n\n const dbNamesToRemove: string[] = [];\n const dbNamesToKeep: string[] = [];\n const deletedClientsToRemove: WritableDeletedClients = [];\n for (const [dbName, [canCollect, deletedClients]] of collectResults) {\n if (canCollect) {\n dbNamesToRemove.push(dbName);\n deletedClientsToRemove.push(...deletedClients);\n } else {\n dbNamesToKeep.push(dbName);\n }\n }\n\n const {errors} = await dropDatabases(\n idbDatabasesStore,\n dbNamesToRemove,\n kvStoreProvider.drop,\n );\n if (errors.length) {\n throw errors[0];\n }\n\n if (deletedClientsToRemove.length > 0) {\n // Add the deleted clients to all the dbs that survived the collection.\n let newDeletedClients: DeletedClients = deletedClientsToRemove;\n for (const name of dbNamesToKeep) {\n const perdag = newDagStore(name, kvStoreProvider.create);\n let dbDeletedClients: DeletedClients;\n try {\n dbDeletedClients = await withWrite(perdag, dagWrite =>\n addDeletedClients(dagWrite, deletedClientsToRemove),\n );\n } catch (e) {\n if (!(e instanceof InvalidRefCountError)) {\n throw e;\n }\n // This database is corrupt. Its own instance will find out and\n // recover the next time it writes to it; here, just skip it and keep\n // collecting into the others, instead of letting one corrupt\n // database break every collection run forever.\n continue;\n } finally {\n await closeIgnoringErrors(perdag);\n }\n newDeletedClients = mergeDeletedClients(\n newDeletedClients,\n dbDeletedClients,\n );\n }\n // normalize and dedupe\n const normalizedDeletedClients = normalizeDeletedClients(newDeletedClients);\n\n // Call the callback with the normalized deleted clients\n await onClientsDeleted(normalizedDeletedClients);\n }\n}\n\nexport async function dropDatabaseInternal(\n name: string,\n idbDatabasesStore: IDBDatabasesStore,\n kvDropStore: DropStore,\n) {\n await kvDropStore(name);\n await idbDatabasesStore.deleteDatabases([name]);\n}\n\nasync function dropDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n namesToRemove: string[],\n kvDropStore: DropStore,\n): Promise<{dropped: string[]; errors: unknown[]}> {\n // Try to remove the databases in parallel. Don't let a single reject fail the\n // other ones. We will check for failures afterwards.\n const dropStoreResults = await Promise.allSettled(\n namesToRemove.map(async name => {\n await dropDatabaseInternal(name, idbDatabasesStore, kvDropStore);\n return name;\n }),\n );\n\n const dropped: string[] = [];\n const errors: unknown[] = [];\n for (const result of dropStoreResults) {\n if (result.status === 'fulfilled') {\n dropped.push(result.value);\n } else {\n errors.push(result.reason);\n }\n }\n\n return {dropped, errors};\n}\n\n/**\n * Creates the dag store used to inspect and update a database during\n * collection. Callers can override it to install store hooks, for example so\n * a corrupt ref count found while writing deleted clients is recovered from.\n * Collection closes every store it creates once it is done with it.\n */\nexport type NewDagStore = (name: string, kvCreateStore: CreateStore) => Store;\n\nfunction defaultNewDagStore(name: string, kvCreateStore: CreateStore): Store {\n const perKvStore = kvCreateStore(name);\n return new StoreImpl(perKvStore, newRandomHash, assertHash);\n}\n\n/**\n * If any client has a recent heartbeat or there are pending mutations we\n * return `[false]`. Otherwise we return `[true, deletedClients]`.\n */\nasync function gatherDatabaseInfoForCollect(\n db: IndexedDBDatabase,\n now: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n kvCreateStore: CreateStore,\n newDagStore: NewDagStore,\n): Promise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n if (db.replicacheFormatVersion > FormatVersion.Latest) {\n return [false];\n }\n\n // If increase the format version we need to decide how to deal with this\n // logic.\n assert(\n db.replicacheFormatVersion === FormatVersion.DD31 ||\n db.replicacheFormatVersion === FormatVersion.V6 ||\n db.replicacheFormatVersion === FormatVersion.V7,\n () =>\n `Expected replicacheFormatVersion to be DD31, V6, or V7, got ${db.replicacheFormatVersion}`,\n );\n // Closed before the database is dropped, so the drop does not find it open.\n const perdag = newDagStore(db.name, kvCreateStore);\n try {\n return await canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery,\n perdag,\n now,\n maxAge,\n );\n } finally {\n await closeIgnoringErrors(perdag);\n }\n}\n\n/**\n * Options for `dropDatabase` and `dropAllDatabases`.\n */\nexport type DropDatabaseOptions = {\n /**\n * Allows providing a custom implementation of the underlying storage layer.\n * Default is `'idb'`.\n */\n kvStore?: 'idb' | 'mem' | StoreProvider | undefined;\n /**\n * Determines how much logging to do. When this is set to `'debug'`,\n * Replicache will also log `'info'` and `'error'` messages. When set to\n * `'info'` we log `'info'` and `'error'` but not `'debug'`. When set to\n * `'error'` we only log `'error'` messages.\n * Default is `'info'`.\n */\n logLevel?: LogLevel | undefined;\n /**\n * Enables custom handling of logs.\n *\n * By default logs are logged to the console. If you would like logs to be\n * sent elsewhere (e.g. to a cloud logging service like DataDog) you can\n * provide an array of {@link LogSink}s. Logs at or above\n * {@link DropDatabaseOptions.logLevel} are sent to each of these {@link LogSink}s.\n * If you would still like logs to go to the console, include\n * `consoleLogSink` in the array.\n *\n * ```ts\n * logSinks: [consoleLogSink, myCloudLogSink],\n * ```\n * Default is `[consoleLogSink]`.\n */\n logSinks?: LogSink[] | undefined;\n};\n\n/**\n * Drops the specified database, from memory as well as from the configured\n * store (see `onStorageFailure`). Rejects if the configured store cannot be\n * opened or dropped.\n * @param dbName The name of the database to drop.\n * @param opts Options for dropping the database.\n */\nexport async function dropDatabase(dbName: string, opts?: DropDatabaseOptions) {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropDatabase: undefined,\n });\n for (const {provider, drops} of dropTargets(logContext, opts)) {\n if (!drops(dbName)) {\n continue;\n }\n const store = new IDBDatabasesStore(provider.create);\n try {\n await dropDatabaseInternal(dbName, store, provider.drop);\n } finally {\n await closeIgnoringErrors(store);\n }\n }\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache, and the in-memory\n * databases instances ran on after a storage failure (see\n * `onStorageFailure`).\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping. Rejects if the configured store\n * cannot be opened.\n */\nexport function dropAllDatabases(opts?: DropDatabaseOptions): Promise<{\n dropped: string[];\n errors: unknown[];\n}> {\n return dropMatchingDatabases(() => true, opts);\n}\n\n/**\n * Like {@link dropAllDatabases}, for the databases whose registry record\n * matches `predicate`.\n */\nexport async function dropMatchingDatabases(\n predicate: (db: IndexedDBDatabase) => boolean,\n opts?: DropDatabaseOptions,\n): Promise<{\n dropped: string[];\n errors: unknown[];\n}> {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropDatabases: undefined,\n });\n const dropped = new Set<string>();\n const errors: unknown[] = [];\n for (const {provider, drops} of dropTargets(logContext, opts)) {\n const store = new IDBDatabasesStore(provider.create);\n try {\n const databases = await store.getDatabases();\n const dbNames = Object.values(databases)\n .filter(db => predicate(db) && drops(db.name))\n .map(db => db.name);\n const result = await dropDatabases(store, dbNames, provider.drop);\n result.dropped.forEach(name => dropped.add(name));\n errors.push(...result.errors);\n } finally {\n await closeIgnoringErrors(store);\n }\n }\n return {dropped: [...dropped], errors};\n}\n\ntype DropTarget = {\n provider: StoreProvider;\n /** Whether a database of this name is dropped from `provider`. */\n drops: (name: string) => boolean;\n};\n\n/**\n * The memory stores instances fell back to first, then the configured store.\n * An instance whose store failed to open runs on memory stores of the same\n * names (see `onStorageFailure`), and memory stores outlive the instance for\n * the life of the process, so a drop clears them whatever the configured\n * store is. Only those: the memory stores of `kvStore: 'mem'` instances are\n * left alone unless `'mem'` is the store being dropped from. The configured\n * store is still dropped, and its failure is reported: it may hold data on\n * disk.\n */\nfunction dropTargets(\n lc: LogContext,\n opts: DropDatabaseOptions | undefined,\n): DropTarget[] {\n const all = () => true;\n if (opts?.kvStore === 'mem') {\n return [{provider: getKVStoreProvider(lc, 'mem'), drops: all}];\n }\n const targets: DropTarget[] = [];\n if (isFallbackStore(getIDBDatabasesDBName())) {\n targets.push({provider: fallbackStoreProvider, drops: isFallbackStore});\n }\n targets.push({provider: getKVStoreProvider(lc, opts?.kvStore), drops: all});\n return targets;\n}\n\n/**\n * A store that failed to open may also fail to close; the drop's or the\n * collection's own result is what the caller needs.\n */\nfunction closeIgnoringErrors(store: {close(): Promise<void>}): Promise<void> {\n return store.close().catch(() => undefined);\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n *\n * @deprecated Use `dropAllDatabases` instead.\n */\nexport function deleteAllReplicacheData(opts?: DropDatabaseOptions) {\n return dropAllDatabases(opts);\n}\n\n/**\n * If there are pending mutations in any of the clients in this db we return\n * `[false]`. If any client has a recent heartbeat we also return `[false]`.\n * Otherwise we return `[true, deletedClients]`.\n */\nfunction canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery: boolean,\n perdag: Store,\n now: number,\n maxAge: number,\n): Promise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n return withRead(perdag, async read => {\n // If mutation recovery is disabled we do not care if there are pending\n // mutations when we decide if we can collect the database.\n if (enableMutationRecovery) {\n const clientGroups = await getClientGroups(read);\n for (const clientGroup of clientGroups.values()) {\n if (clientGroupHasPendingMutations(clientGroup)) {\n return [false];\n }\n }\n }\n\n const clients = await getClients(read);\n\n // Don't collect if any client has a recent heartbeat (is still active).\n // This is defense in depth - normally lastOpenedTimestampMS is kept fresh\n // by the heartbeat, but this check protects against edge cases.\n for (const [, client] of clients) {\n if (now - client.heartbeatTimestampMs < maxAge) {\n return [false];\n }\n }\n\n const existingDeletedClients = await getDeletedClients(read);\n const deletedClients: WritableDeletedClients = [...existingDeletedClients];\n\n // Add all current clients to the deleted clients list\n for (const [clientID, client] of clients) {\n deletedClients.push({\n clientID,\n clientGroupID: client.clientGroupID,\n });\n }\n\n // The normalization (deduping and sorting) will be done when storing\n return [true, deletedClients];\n });\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { asyncIterableToArray } from "./async-iterable-to-array.js";
2
1
  import { throwIfClosed } from "./transaction-closed-error.js";
2
+ import { asyncIterableToArray } from "./async-iterable-to-array.js";
3
3
  import { isScanIndexOptions, normalizeScanOptionIndexedStartKey } from "./scan-options.js";
4
4
  import "./transactions.js";
5
5
  import "compare-utf8";
@@ -1,10 +1,10 @@
1
- import { getNonCryptoRandomValues } from "../../../shared/src/random-values.js";
1
+ import { getRandomValues } from "../../../shared/src/random-values.js";
2
2
  //#region ../replicache/src/sync/request-id.ts
3
3
  var sessionID = "";
4
4
  function getSessionID() {
5
5
  if (sessionID === "") {
6
6
  const buf = /* @__PURE__ */ new Uint8Array(4);
7
- getNonCryptoRandomValues(buf);
7
+ getRandomValues(buf);
8
8
  sessionID = Array.from(buf, (x) => x.toString(16)).join("");
9
9
  }
10
10
  return sessionID;
@@ -1 +1 @@
1
- {"version":3,"file":"request-id.js","names":[],"sources":["../../../../../replicache/src/sync/request-id.ts"],"sourcesContent":["import {getNonCryptoRandomValues} from '../../../shared/src/random-values.ts';\nimport type {ClientID} from './ids.ts';\n\nlet sessionID = '';\nfunction getSessionID() {\n if (sessionID === '') {\n const buf = new Uint8Array(4);\n getNonCryptoRandomValues(buf);\n sessionID = Array.from(buf, x => x.toString(16)).join('');\n }\n return sessionID;\n}\n\nconst REQUEST_COUNTERS: Map<string, number> = new Map();\n\n/**\n * Returns a new requestID of the form <client ID>-<session ID>-<request\n * count>. The request count enables one to find the request following or\n * preceding a given request. The sessionid scopes the request count, ensuring\n * the requestID is probabilistically unique across restarts (which is good\n * enough).\n */\nexport function newRequestID(clientID: ClientID): string {\n const counter = REQUEST_COUNTERS.get(clientID) ?? 0;\n REQUEST_COUNTERS.set(clientID, counter + 1);\n return `${clientID}-${getSessionID()}-${counter}`;\n}\n"],"mappings":";;AAGA,IAAI,YAAY;AAChB,SAAS,eAAe;CACtB,IAAI,cAAc,IAAI;EACpB,MAAM,sBAAM,IAAI,WAAW,CAAC;EAC5B,yBAAyB,GAAG;EAC5B,YAAY,MAAM,KAAK,MAAK,MAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE;CAC1D;CACA,OAAO;AACT;AAEA,IAAM,mCAAwC,IAAI,IAAI;;;;;;;;AAStD,SAAgB,aAAa,UAA4B;CACvD,MAAM,UAAU,iBAAiB,IAAI,QAAQ,KAAK;CAClD,iBAAiB,IAAI,UAAU,UAAU,CAAC;CAC1C,OAAO,GAAG,SAAS,GAAG,aAAa,EAAE,GAAG;AAC1C"}
1
+ {"version":3,"file":"request-id.js","names":[],"sources":["../../../../../replicache/src/sync/request-id.ts"],"sourcesContent":["import {getRandomValues} from '../../../shared/src/random-values.ts';\nimport type {ClientID} from './ids.ts';\n\nlet sessionID = '';\nfunction getSessionID() {\n if (sessionID === '') {\n const buf = new Uint8Array(4);\n getRandomValues(buf);\n sessionID = Array.from(buf, x => x.toString(16)).join('');\n }\n return sessionID;\n}\n\nconst REQUEST_COUNTERS: Map<string, number> = new Map();\n\n/**\n * Returns a new requestID of the form <client ID>-<session ID>-<request\n * count>. The request count enables one to find the request following or\n * preceding a given request. The sessionid scopes the request count, ensuring\n * the requestID is probabilistically unique across restarts (which is good\n * enough).\n */\nexport function newRequestID(clientID: ClientID): string {\n const counter = REQUEST_COUNTERS.get(clientID) ?? 0;\n REQUEST_COUNTERS.set(clientID, counter + 1);\n return `${clientID}-${getSessionID()}-${counter}`;\n}\n"],"mappings":";;AAGA,IAAI,YAAY;AAChB,SAAS,eAAe;CACtB,IAAI,cAAc,IAAI;EACpB,MAAM,sBAAM,IAAI,WAAW,CAAC;EAC5B,gBAAgB,GAAG;EACnB,YAAY,MAAM,KAAK,MAAK,MAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE;CAC1D;CACA,OAAO;AACT;AAEA,IAAM,mCAAwC,IAAI,IAAI;;;;;;;;AAStD,SAAgB,aAAa,UAA4B;CACvD,MAAM,UAAU,iBAAiB,IAAI,QAAQ,KAAK;CAClD,iBAAiB,IAAI,UAAU,UAAU,CAAC;CAC1C,OAAO,GAAG,SAAS,GAAG,aAAa,EAAE,GAAG;AAC1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"bigint-json.d.ts","sourceRoot":"","sources":["../../../../shared/src/bigint-json.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAYjC,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,SAAS,EAAE,GACpB,UAAU,CAAC;AAEf,MAAM,MAAM,UAAU,GAAG;IAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;CAAC,CAAC;AAE1E,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAY5C,CAAC;AAEH,eAAO,MAAM,gBAAgB,6CAAwC,CAAC;AAUtE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAsB3D;AAED;;;GAGG;AACH,wBAAgB,KAAK,CACnB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,GAC3C,SAAS,CAIX;AAOD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,OAAO,EACZ,QAAQ,CAAC,EACL,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GACnB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,GAC1C,IAAI,EACR,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,UAGzB;AAED,eAAO,MAAM,UAAU;;;CAGb,CAAC"}
1
+ {"version":3,"file":"bigint-json.d.ts","sourceRoot":"","sources":["../../../../shared/src/bigint-json.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAYjC,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,SAAS,EAAE,GACpB,UAAU,CAAC;AAEf,MAAM,MAAM,UAAU,GAAG;IAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;CAAC,CAAC;AAE1E,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAY5C,CAAC;AAEH,eAAO,MAAM,gBAAgB,6CAAwC,CAAC;AAUtE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAsB3D;AAED;;;GAGG;AACH,wBAAgB,KAAK,CACnB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,GAC3C,SAAS,CAIX;AAcD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,OAAO,EACZ,QAAQ,CAAC,EACL,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GACnB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,GAC1C,IAAI,EACR,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,UAGzB;AAED,eAAO,MAAM,UAAU;;;CAGb,CAAC"}
@@ -51,6 +51,7 @@ function parse$1(str, reviver) {
51
51
  }
52
52
  function customSerializer(_, v, type) {
53
53
  if (type === "bigint") return v.toString();
54
+ if (type === "number" && Number.isInteger(v) && !Number.isSafeInteger(v)) return v.toExponential();
54
55
  }
55
56
  /**
56
57
  * Stringifies objects to JSON, supporting objects containing bigint values.
@@ -1 +1 @@
1
- {"version":3,"file":"bigint-json.js","names":["v","v.readonly","parse","customParse","stringify","customStringify"],"sources":["../../../../shared/src/bigint-json.ts"],"sourcesContent":["/**\n * Background for using `json-custom-numbers`:\n *\n * https://neon.tech/blog/parsing-json-from-postgres-in-js\n */\nimport {\n parse as customParse,\n stringify as customStringify,\n} from 'json-custom-numbers';\nimport * as v from './valita.ts';\n\nfunction numberParser(_: unknown, v: string) {\n const n = +v;\n if (n >= Number.MIN_SAFE_INTEGER && n <= Number.MAX_SAFE_INTEGER) return n;\n if (v.includes('.') || v.includes('e') || v.includes('E')) {\n return n;\n }\n return BigInt(v);\n}\n\n// Variant of postgres.JSONValue adapted to include bigints\nexport type JSONValue =\n | null\n | string\n | number\n | bigint\n | boolean\n | readonly JSONValue[]\n | JSONObject;\n\nexport type JSONObject = {readonly [prop: string]: JSONValue | undefined};\n\nexport const jsonValueSchema: v.Type<JSONValue> = v.lazy(() => {\n const jsonObjectSchema = v.readonly(v.record(jsonValueSchema));\n\n return v.union(\n v.null(),\n v.string(),\n v.number(),\n v.bigint(),\n v.boolean(),\n v.readonly(v.array(jsonValueSchema)),\n jsonObjectSchema,\n );\n});\n\nexport const jsonObjectSchema = v.readonly(v.record(jsonValueSchema));\n\n// Integers of up to 15 digits are safe, so only a run of 16 or more digits can\n// be an integer that needs the (much slower) custom number parsing.\nconst UNSAFE_INTEGER_DIGITS = 16;\n\nfunction isDigit(c: number): boolean {\n return c >= 48 && c <= 57;\n}\n\n/**\n * Whether `str` has a run of 16 or more digits. Every such run covers one of\n * every 16 consecutive indices, so only every 16th char needs to be checked and\n * a digit there is expanded to its full run. This is several times faster than\n * `/\\d{16}/`, which restarts at every digit of a shorter run.\n *\n * A run inside a JSON string also matches; that merely takes the slow path.\n */\nexport function hasUnsafeIntegerDigits(str: string): boolean {\n const n = str.length;\n for (let i = UNSAFE_INTEGER_DIGITS - 1; i < n; i += UNSAFE_INTEGER_DIGITS) {\n if (!isDigit(str.charCodeAt(i))) {\n continue;\n }\n let start = i;\n while (start > 0 && isDigit(str.charCodeAt(start - 1))) {\n start--;\n }\n let end = i + 1;\n while (end < n && isDigit(str.charCodeAt(end))) {\n end++;\n }\n if (end - start >= UNSAFE_INTEGER_DIGITS) {\n return true;\n }\n // The next run starts after `end`, and covers one of every 16 indices from\n // there on.\n i = end - 1;\n }\n return false;\n}\n\n/**\n * Parses JSON strings that may contain arbitrarily large integers. Integers\n * larger than {@link Number.MAX_SAFE_INTEGER} are deserialized as a `bigint`.\n */\nexport function parse(\n str: string,\n reviver?: (k: string, v: unknown) => unknown,\n): JSONValue {\n return hasUnsafeIntegerDigits(str)\n ? customParse(str, reviver, numberParser)\n : JSON.parse(str, reviver);\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nfunction customSerializer(_: string, v: any, type: string) {\n if (type === 'bigint') return v.toString();\n}\n\n/**\n * Stringifies objects to JSON, supporting objects containing bigint values.\n * Note that the resulting JSON string may not be deserializable by\n * all environments, but it is supported by Postgres. The string should be\n * deserialized with the corresponding {@link parse} method that will represent\n * large numbers as bigints. From there it is up to the application to suitably\n * handle bigints passed to downstream logic.\n */\nexport function stringify(\n obj: unknown,\n replacer?:\n | (string | number)[]\n | ((key: string, value: unknown) => unknown)\n | null,\n indent?: string | number,\n) {\n return customStringify(obj, replacer, indent, customSerializer);\n}\n\nexport const BigIntJSON = {\n parse,\n stringify,\n} as const;\n"],"mappings":";;;;;;;;AAWA,SAAS,aAAa,GAAY,GAAW;CAC3C,MAAM,IAAI,CAAC;CACX,IAAI,KAAK,OAAO,oBAAoB,KAAK,OAAO,kBAAkB,OAAO;CACzE,IAAI,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,GAAG,GACtD,OAAO;CAET,OAAO,OAAO,CAAC;AACjB;AAcA,IAAa,kBAAqCA,eAAE,WAAW;CAC7D,MAAM,mBAAmBC,SAAWD,eAAE,OAAO,eAAe,CAAC;CAE7D,OAAOA,eAAE,MACPA,eAAE,KAAK,GACPA,eAAE,OAAO,GACTA,eAAE,OAAO,GACTA,eAAE,OAAO,GACTA,eAAE,QAAQ,GACVC,SAAWD,eAAE,MAAM,eAAe,CAAC,GACnC,gBACF;AACF,CAAC;AAED,IAAa,mBAAmBC,SAAWD,eAAE,OAAO,eAAe,CAAC;AAIpE,IAAM,wBAAwB;AAE9B,SAAS,QAAQ,GAAoB;CACnC,OAAO,KAAK,MAAM,KAAK;AACzB;;;;;;;;;AAUA,SAAgB,uBAAuB,KAAsB;CAC3D,MAAM,IAAI,IAAI;CACd,KAAK,IAAI,IAAI,IAA2B,IAAI,GAAG,KAAK,uBAAuB;EACzE,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,CAAC,GAC5B;EAEF,IAAI,QAAQ;EACZ,OAAO,QAAQ,KAAK,QAAQ,IAAI,WAAW,QAAQ,CAAC,CAAC,GACnD;EAEF,IAAI,MAAM,IAAI;EACd,OAAO,MAAM,KAAK,QAAQ,IAAI,WAAW,GAAG,CAAC,GAC3C;EAEF,IAAI,MAAM,SAAS,uBACjB,OAAO;EAIT,IAAI,MAAM;CACZ;CACA,OAAO;AACT;;;;;AAMA,SAAgBE,QACd,KACA,SACW;CACX,OAAO,uBAAuB,GAAG,IAC7BC,MAAY,KAAK,SAAS,YAAY,IACtC,KAAK,MAAM,KAAK,OAAO;AAC7B;AAGA,SAAS,iBAAiB,GAAW,GAAQ,MAAc;CACzD,IAAI,SAAS,UAAU,OAAO,EAAE,SAAS;AAC3C;;;;;;;;;AAUA,SAAgBC,YACd,KACA,UAIA,QACA;CACA,OAAOC,UAAgB,KAAK,UAAU,QAAQ,gBAAgB;AAChE;AAEA,IAAa,aAAa;CACxB,OAAA;CACA,WAAA;AACF"}
1
+ {"version":3,"file":"bigint-json.js","names":["v","v.readonly","parse","customParse","stringify","customStringify"],"sources":["../../../../shared/src/bigint-json.ts"],"sourcesContent":["/**\n * Background for using `json-custom-numbers`:\n *\n * https://neon.tech/blog/parsing-json-from-postgres-in-js\n */\nimport {\n parse as customParse,\n stringify as customStringify,\n} from 'json-custom-numbers';\nimport * as v from './valita.ts';\n\nfunction numberParser(_: unknown, v: string) {\n const n = +v;\n if (n >= Number.MIN_SAFE_INTEGER && n <= Number.MAX_SAFE_INTEGER) return n;\n if (v.includes('.') || v.includes('e') || v.includes('E')) {\n return n;\n }\n return BigInt(v);\n}\n\n// Variant of postgres.JSONValue adapted to include bigints\nexport type JSONValue =\n | null\n | string\n | number\n | bigint\n | boolean\n | readonly JSONValue[]\n | JSONObject;\n\nexport type JSONObject = {readonly [prop: string]: JSONValue | undefined};\n\nexport const jsonValueSchema: v.Type<JSONValue> = v.lazy(() => {\n const jsonObjectSchema = v.readonly(v.record(jsonValueSchema));\n\n return v.union(\n v.null(),\n v.string(),\n v.number(),\n v.bigint(),\n v.boolean(),\n v.readonly(v.array(jsonValueSchema)),\n jsonObjectSchema,\n );\n});\n\nexport const jsonObjectSchema = v.readonly(v.record(jsonValueSchema));\n\n// Integers of up to 15 digits are safe, so only a run of 16 or more digits can\n// be an integer that needs the (much slower) custom number parsing.\nconst UNSAFE_INTEGER_DIGITS = 16;\n\nfunction isDigit(c: number): boolean {\n return c >= 48 && c <= 57;\n}\n\n/**\n * Whether `str` has a run of 16 or more digits. Every such run covers one of\n * every 16 consecutive indices, so only every 16th char needs to be checked and\n * a digit there is expanded to its full run. This is several times faster than\n * `/\\d{16}/`, which restarts at every digit of a shorter run.\n *\n * A run inside a JSON string also matches; that merely takes the slow path.\n */\nexport function hasUnsafeIntegerDigits(str: string): boolean {\n const n = str.length;\n for (let i = UNSAFE_INTEGER_DIGITS - 1; i < n; i += UNSAFE_INTEGER_DIGITS) {\n if (!isDigit(str.charCodeAt(i))) {\n continue;\n }\n let start = i;\n while (start > 0 && isDigit(str.charCodeAt(start - 1))) {\n start--;\n }\n let end = i + 1;\n while (end < n && isDigit(str.charCodeAt(end))) {\n end++;\n }\n if (end - start >= UNSAFE_INTEGER_DIGITS) {\n return true;\n }\n // The next run starts after `end`, and covers one of every 16 indices from\n // there on.\n i = end - 1;\n }\n return false;\n}\n\n/**\n * Parses JSON strings that may contain arbitrarily large integers. Integers\n * larger than {@link Number.MAX_SAFE_INTEGER} are deserialized as a `bigint`.\n */\nexport function parse(\n str: string,\n reviver?: (k: string, v: unknown) => unknown,\n): JSONValue {\n return hasUnsafeIntegerDigits(str)\n ? customParse(str, reviver, numberParser)\n : JSON.parse(str, reviver);\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nfunction customSerializer(_: string, v: any, type: string) {\n if (type === 'bigint') return v.toString();\n // An integer beyond the safe range is a rounded double if it is a number, so\n // it is written in a form that parse() keeps as a number. As digits it would\n // come back as a bigint, and not even the same value, since a double prints\n // only the digits that identify it: 2^62 is 4611686018427388000.\n if (type === 'number' && Number.isInteger(v) && !Number.isSafeInteger(v)) {\n return v.toExponential();\n }\n}\n\n/**\n * Stringifies objects to JSON, supporting objects containing bigint values.\n * Note that the resulting JSON string may not be deserializable by\n * all environments, but it is supported by Postgres. The string should be\n * deserialized with the corresponding {@link parse} method that will represent\n * large numbers as bigints. From there it is up to the application to suitably\n * handle bigints passed to downstream logic.\n */\nexport function stringify(\n obj: unknown,\n replacer?:\n | (string | number)[]\n | ((key: string, value: unknown) => unknown)\n | null,\n indent?: string | number,\n) {\n return customStringify(obj, replacer, indent, customSerializer);\n}\n\nexport const BigIntJSON = {\n parse,\n stringify,\n} as const;\n"],"mappings":";;;;;;;;AAWA,SAAS,aAAa,GAAY,GAAW;CAC3C,MAAM,IAAI,CAAC;CACX,IAAI,KAAK,OAAO,oBAAoB,KAAK,OAAO,kBAAkB,OAAO;CACzE,IAAI,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,GAAG,GACtD,OAAO;CAET,OAAO,OAAO,CAAC;AACjB;AAcA,IAAa,kBAAqCA,eAAE,WAAW;CAC7D,MAAM,mBAAmBC,SAAWD,eAAE,OAAO,eAAe,CAAC;CAE7D,OAAOA,eAAE,MACPA,eAAE,KAAK,GACPA,eAAE,OAAO,GACTA,eAAE,OAAO,GACTA,eAAE,OAAO,GACTA,eAAE,QAAQ,GACVC,SAAWD,eAAE,MAAM,eAAe,CAAC,GACnC,gBACF;AACF,CAAC;AAED,IAAa,mBAAmBC,SAAWD,eAAE,OAAO,eAAe,CAAC;AAIpE,IAAM,wBAAwB;AAE9B,SAAS,QAAQ,GAAoB;CACnC,OAAO,KAAK,MAAM,KAAK;AACzB;;;;;;;;;AAUA,SAAgB,uBAAuB,KAAsB;CAC3D,MAAM,IAAI,IAAI;CACd,KAAK,IAAI,IAAI,IAA2B,IAAI,GAAG,KAAK,uBAAuB;EACzE,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,CAAC,GAC5B;EAEF,IAAI,QAAQ;EACZ,OAAO,QAAQ,KAAK,QAAQ,IAAI,WAAW,QAAQ,CAAC,CAAC,GACnD;EAEF,IAAI,MAAM,IAAI;EACd,OAAO,MAAM,KAAK,QAAQ,IAAI,WAAW,GAAG,CAAC,GAC3C;EAEF,IAAI,MAAM,SAAS,uBACjB,OAAO;EAIT,IAAI,MAAM;CACZ;CACA,OAAO;AACT;;;;;AAMA,SAAgBE,QACd,KACA,SACW;CACX,OAAO,uBAAuB,GAAG,IAC7BC,MAAY,KAAK,SAAS,YAAY,IACtC,KAAK,MAAM,KAAK,OAAO;AAC7B;AAGA,SAAS,iBAAiB,GAAW,GAAQ,MAAc;CACzD,IAAI,SAAS,UAAU,OAAO,EAAE,SAAS;CAKzC,IAAI,SAAS,YAAY,OAAO,UAAU,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,GACrE,OAAO,EAAE,cAAc;AAE3B;;;;;;;;;AAUA,SAAgBC,YACd,KACA,UAIA,QACA;CACA,OAAOC,UAAgB,KAAK,UAAU,QAAQ,gBAAgB;AAChE;AAEA,IAAa,aAAa;CACxB,OAAA;CACA,WAAA;AACF"}
@@ -30,6 +30,27 @@ export declare class BTreeSet<K> {
30
30
  valuesReversed(): IterableIterator<K>;
31
31
  valuesFromReversed(highestKey?: K, inclusive?: boolean): IterableIterator<K>;
32
32
  [Symbol.iterator](): IterableIterator<K>;
33
+ /**
34
+ * Returns every key, in order, as a fresh array the caller owns (sorting it
35
+ * in place is safe).
36
+ *
37
+ * This is the inverse of `fromSorted`: it collects the leaves' `keys` arrays
38
+ * and joins them with `concat`, instead of yielding one key at a time. Every
39
+ * key the iterator yields pays for a `next()` call that walks a node queue
40
+ * and allocates a result object, and on an interpreter that is not something
41
+ * a JS loop can win back -- the work has to happen in native code.
42
+ *
43
+ * Measured on an Android emulator (Hermes, release), 90,000 rows per call:
44
+ * 5.76ms through the iterator, 1.73ms for a per-element JS copy loop into a
45
+ * pre-sized array, 0.56ms for this. So a JS loop buys 3.3x and native copying
46
+ * 10x; at 30,000 rows, where the join is a single round, it is 20x. V8 JITs
47
+ * such a loop to near-memcpy, so the choice barely registers there and is
48
+ * worth 3x on a phone.
49
+ *
50
+ * Complexity: O(size). Nothing collected is mutated, so shared/cloned nodes
51
+ * are safe, and the arrays are copied rather than handed out.
52
+ */
53
+ toArray(): K[];
33
54
  /**
34
55
  * Builds a BTreeSet from a pre-sorted iterable in O(N) by constructing
35
56
  * the tree bottom-up. The caller must ensure entries are sorted by
@@ -1 +1 @@
1
- {"version":3,"file":"btree-set.d.ts","sourceRoot":"","sources":["../../../../shared/src/btree-set.ts"],"names":[],"mappings":"AAIA,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;AAE5C,qBAAa,QAAQ,CAAC,CAAC;;IAErB,IAAI,EAAE,MAAM,CAAK;IAEjB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAEnC,YAAY,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAOnE;IAED,+CAA+C;IAC/C,KAAK,SAGJ;IAED,KAAK,gBAMJ;IAED,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAEzB;IAED,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAOhB;IAED;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAEnB;IAED;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAEtB;IAuBD,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAE1B;IAED,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAE5B;IAED,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,SAAS,GAAE,OAAc,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAExE;IAED,cAAc,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAQpC;IAED,kBAAkB,CAChB,UAAU,CAAC,EAAE,CAAC,EACd,SAAS,GAAE,OAAc,GACxB,gBAAgB,CAAC,CAAC,CAAC,CAQrB;IAOD,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAEvC;IAED;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EACjB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC,GACzB,QAAQ,CAAC,CAAC,CAAC,CA4Cb;CACF"}
1
+ {"version":3,"file":"btree-set.d.ts","sourceRoot":"","sources":["../../../../shared/src/btree-set.ts"],"names":[],"mappings":"AAoBA,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;AAE5C,qBAAa,QAAQ,CAAC,CAAC;;IAErB,IAAI,EAAE,MAAM,CAAK;IAEjB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAEnC,YAAY,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAOnE;IAED,+CAA+C;IAC/C,KAAK,SAGJ;IAED,KAAK,gBAMJ;IAED,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAEzB;IAED,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAOhB;IAED;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAEnB;IAED;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAEtB;IAuBD,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAE1B;IAED,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAE5B;IAED,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,SAAS,GAAE,OAAc,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAExE;IAED,cAAc,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAQpC;IAED,kBAAkB,CAChB,UAAU,CAAC,EAAE,CAAC,EACd,SAAS,GAAE,OAAc,GACxB,gBAAgB,CAAC,CAAC,CAAC,CAQrB;IAOD,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAEvC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,IAAI,CAAC,EAAE,CAuBb;IAED;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EACjB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC,GACzB,QAAQ,CAAC,CAAC,CAAC,CA4Cb;CACF"}
@@ -1,6 +1,15 @@
1
1
  import { assert } from "./asserts.js";
2
2
  //#region ../shared/src/btree-set.ts
3
3
  var MAX_NODE_SIZE = 32;
4
+ /**
5
+ * How many arrays `toArray` hands `concat` at once. Engines cap how many
6
+ * arguments a call may spread, so the join is chunked rather than passing one
7
+ * argument per leaf.
8
+ */
9
+ var CONCAT_CHUNK = 1024;
10
+ function concatAll(arrays) {
11
+ return [].concat(...arrays);
12
+ }
4
13
  var BTreeSet = class BTreeSet {
5
14
  #root = emptyLeaf;
6
15
  size = 0;
@@ -87,6 +96,39 @@ var BTreeSet = class BTreeSet {
87
96
  return this.keys();
88
97
  }
89
98
  /**
99
+ * Returns every key, in order, as a fresh array the caller owns (sorting it
100
+ * in place is safe).
101
+ *
102
+ * This is the inverse of `fromSorted`: it collects the leaves' `keys` arrays
103
+ * and joins them with `concat`, instead of yielding one key at a time. Every
104
+ * key the iterator yields pays for a `next()` call that walks a node queue
105
+ * and allocates a result object, and on an interpreter that is not something
106
+ * a JS loop can win back -- the work has to happen in native code.
107
+ *
108
+ * Measured on an Android emulator (Hermes, release), 90,000 rows per call:
109
+ * 5.76ms through the iterator, 1.73ms for a per-element JS copy loop into a
110
+ * pre-sized array, 0.56ms for this. So a JS loop buys 3.3x and native copying
111
+ * 10x; at 30,000 rows, where the join is a single round, it is 20x. V8 JITs
112
+ * such a loop to near-memcpy, so the choice barely registers there and is
113
+ * worth 3x on a phone.
114
+ *
115
+ * Complexity: O(size). Nothing collected is mutated, so shared/cloned nodes
116
+ * are safe, and the arrays are copied rather than handed out.
117
+ */
118
+ toArray() {
119
+ const root = this.#root;
120
+ if (!root.isInternal()) return root.keys.slice();
121
+ const leaves = [];
122
+ root.collectLeafKeys(leaves);
123
+ let level = leaves;
124
+ while (level.length > 1) {
125
+ const next = [];
126
+ for (let i = 0; i < level.length; i += CONCAT_CHUNK) next.push(concatAll(level.slice(i, i + CONCAT_CHUNK)));
127
+ level = next;
128
+ }
129
+ return level[0] ?? [];
130
+ }
131
+ /**
90
132
  * Builds a BTreeSet from a pre-sorted iterable in O(N) by constructing
91
133
  * the tree bottom-up. The caller must ensure entries are sorted by
92
134
  * `comparator`; violating this produces an invalid tree.
@@ -268,6 +310,14 @@ var BNode = class BNode {
268
310
  clone() {
269
311
  return new BNode(this.keys.slice(0));
270
312
  }
313
+ /**
314
+ * Appends this node's `keys` arrays, in order, to `out`. Read-only: the
315
+ * arrays are not copied here and must not be mutated by the caller, which is
316
+ * why `toArray` only ever passes them to `concat`.
317
+ */
318
+ collectLeafKeys(out) {
319
+ out.push(this.keys);
320
+ }
271
321
  get(key, tree) {
272
322
  const i = indexOf(key, this.keys, -1, tree.comparator);
273
323
  return i < 0 ? void 0 : this.keys[i];
@@ -352,6 +402,10 @@ var BNodeInternal = class BNodeInternal extends BNode {
352
402
  minKey() {
353
403
  return this.children[0].minKey();
354
404
  }
405
+ collectLeafKeys(out) {
406
+ const { children } = this;
407
+ for (let i = 0; i < children.length; i++) children[i].collectLeafKeys(out);
408
+ }
355
409
  get(key, tree) {
356
410
  const i = indexOf(key, this.keys, 0, tree.comparator);
357
411
  const { children } = this;
@@ -1 +1 @@
1
- {"version":3,"file":"btree-set.js","names":[],"sources":["../../../../shared/src/btree-set.ts"],"sourcesContent":["import {assert} from './asserts.ts';\n\nconst MAX_NODE_SIZE = 32;\n\ntype Comparator<K> = (a: K, b: K) => number;\n\nexport class BTreeSet<K> {\n #root: BNode<K> = emptyLeaf as BNode<K>;\n size: number = 0;\n\n readonly comparator: Comparator<K>;\n\n constructor(comparator: Comparator<K>, entries?: IterableIterator<K>) {\n this.comparator = comparator;\n if (entries) {\n for (const key of entries) {\n this.add(key);\n }\n }\n }\n\n /** Releases the tree so that its size is 0. */\n clear() {\n this.#root = emptyLeaf as BNode<K>;\n this.size = 0;\n }\n\n clone() {\n this.#root.isShared = true;\n const ret = new BTreeSet<K>(this.comparator);\n ret.#root = this.#root;\n ret.size = this.size;\n return ret;\n }\n\n get(key: K): K | undefined {\n return this.#root.get(key, this);\n }\n\n add(key: K): this {\n if (this.#root.isShared) this.#root = this.#root.clone();\n const result = this.#root.set(key, this);\n if (result === null) return this;\n // Root node has split, so create a new root node.\n this.#root = new BNodeInternal<K>([this.#root, result]);\n return this;\n }\n\n /**\n * Returns true if the key exists in the B+ tree, false if not.\n * Use get() for best performance; use has() if you need to\n * distinguish between \"undefined value\" and \"key not present\".\n * @param key Key to detect\n * @description Computational complexity: O(log size)\n */\n has(key: K): boolean {\n return this.#root.has(key, this);\n }\n\n /**\n * Removes a single key-value pair from the B+ tree.\n * @param key Key to find\n * @returns true if a pair was found and removed, false otherwise.\n * @description Computational complexity: O(log size)\n */\n delete(key: K): boolean {\n return this.#delete(key);\n }\n\n #delete(key: K): boolean {\n let root = this.#root;\n if (root.isShared) {\n this.#root = root = root.clone();\n }\n try {\n return root.delete(key, this);\n } finally {\n let isShared;\n while (root.keys.length <= 1 && root.isInternal()) {\n isShared ||= root.isShared;\n this.#root = root =\n root.keys.length === 0 ? emptyLeaf : root.children[0];\n }\n // If any ancestor of the new root was shared, the new root must also be shared\n if (isShared) {\n root.isShared = true;\n }\n }\n }\n\n keys(): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, undefined, true);\n }\n\n values(): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, undefined, true);\n }\n\n valuesFrom(lowestKey?: K, inclusive: boolean = true): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, lowestKey, inclusive);\n }\n\n valuesReversed(): IterableIterator<K> {\n return valuesFromReversed(\n this.#maxKey(),\n this.#root,\n this.comparator,\n undefined,\n true,\n );\n }\n\n valuesFromReversed(\n highestKey?: K,\n inclusive: boolean = true,\n ): IterableIterator<K> {\n return valuesFromReversed(\n this.#maxKey(),\n this.#root,\n this.comparator,\n highestKey,\n inclusive,\n );\n }\n\n /** Gets the highest key in the tree. Complexity: O(1) */\n #maxKey(): K | undefined {\n return this.#root.maxKey();\n }\n\n [Symbol.iterator](): IterableIterator<K> {\n return this.keys();\n }\n\n /**\n * Builds a BTreeSet from a pre-sorted iterable in O(N) by constructing\n * the tree bottom-up. The caller must ensure entries are sorted by\n * `comparator`; violating this produces an invalid tree.\n */\n static fromSorted<K>(\n comparator: Comparator<K>,\n sortedEntries: Iterable<K>,\n ): BTreeSet<K> {\n const tree = new BTreeSet<K>(comparator);\n\n // Stream through entries, flushing a leaf node each time we fill MAX_NODE_SIZE keys.\n const leaves: BNode<K>[] = [];\n let currentKeys: K[] = [];\n let size = 0;\n\n for (const key of sortedEntries) {\n currentKeys.push(key);\n if (currentKeys.length === MAX_NODE_SIZE) {\n leaves.push(new BNode<K>(currentKeys));\n currentKeys = [];\n }\n size++;\n }\n\n if (currentKeys.length > 0) {\n leaves.push(new BNode<K>(currentKeys));\n }\n\n if (leaves.length === 0) return tree;\n\n tree.size = size;\n\n if (leaves.length === 1) {\n tree.#root = leaves[0];\n return tree;\n }\n\n // Build internal nodes bottom-up until a single root remains.\n let currentLevel: BNode<K>[] = leaves;\n while (currentLevel.length > 1) {\n const nextLevel: BNodeInternal<K>[] = [];\n for (let i = 0; i < currentLevel.length; i += MAX_NODE_SIZE) {\n nextLevel.push(\n new BNodeInternal<K>(currentLevel.slice(i, i + MAX_NODE_SIZE)),\n );\n }\n currentLevel = nextLevel;\n }\n\n tree.#root = currentLevel[0];\n return tree;\n }\n}\n\nclass BTreeForwardIterator<K> implements IterableIterator<K> {\n readonly #nodeQueue: BNode<K>[][];\n readonly #nodeIndex: number[];\n #leaf: BNode<K>;\n #i: number;\n\n constructor(\n nodeQueue: BNode<K>[][],\n nodeIndex: number[],\n leaf: BNode<K>,\n startI: number,\n ) {\n this.#nodeQueue = nodeQueue;\n this.#nodeIndex = nodeIndex;\n this.#leaf = leaf;\n this.#i = startI;\n }\n\n next(): IteratorResult<K> {\n for (;;) {\n if (++this.#i < this.#leaf.keys.length) {\n return {done: false, value: this.#leaf.keys[this.#i]};\n }\n\n let level = -1;\n for (;;) {\n if (++level >= this.#nodeQueue.length) {\n return {done: true, value: undefined as unknown as K};\n }\n if (++this.#nodeIndex[level] < this.#nodeQueue[level].length) {\n break;\n }\n }\n for (; level > 0; level--) {\n this.#nodeQueue[level - 1] = (\n this.#nodeQueue[level][this.#nodeIndex[level]] as BNodeInternal<K>\n ).children;\n this.#nodeIndex[level - 1] = 0;\n }\n this.#leaf = this.#nodeQueue[0][this.#nodeIndex[0]];\n this.#i = -1;\n }\n }\n\n [Symbol.iterator]() {\n return this;\n }\n}\n\nclass BTreeReverseIterator<K> implements IterableIterator<K> {\n readonly #nodeQueue: BNode<K>[][];\n readonly #nodeIndex: number[];\n #leaf: BNode<K>;\n #i: number;\n\n constructor(\n nodeQueue: BNode<K>[][],\n nodeIndex: number[],\n leaf: BNode<K>,\n startI: number,\n ) {\n this.#nodeQueue = nodeQueue;\n this.#nodeIndex = nodeIndex;\n this.#leaf = leaf;\n this.#i = startI;\n }\n\n next(): IteratorResult<K> {\n for (;;) {\n if (--this.#i >= 0) {\n return {done: false, value: this.#leaf.keys[this.#i]};\n }\n\n let level;\n // Advance to the next leaf node\n for (level = -1; ;) {\n if (++level >= this.#nodeQueue.length) {\n return {done: true, value: undefined as unknown as K};\n }\n if (--this.#nodeIndex[level] >= 0) {\n break;\n }\n }\n for (; level > 0; level--) {\n this.#nodeQueue[level - 1] = (\n this.#nodeQueue[level][this.#nodeIndex[level]] as BNodeInternal<K>\n ).children;\n this.#nodeIndex[level - 1] = this.#nodeQueue[level - 1].length - 1;\n }\n this.#leaf = this.#nodeQueue[0][this.#nodeIndex[0]];\n this.#i = this.#leaf.keys.length;\n }\n }\n\n [Symbol.iterator]() {\n return this;\n }\n}\n\nfunction valuesFrom<K>(\n root: BNode<K>,\n comparator: Comparator<K>,\n lowestKey: K | undefined,\n inclusive: boolean,\n): IterableIterator<K> {\n const info = findPath(lowestKey, root, comparator);\n if (info === undefined) {\n return iterator<K>(() => ({done: true, value: undefined}));\n }\n\n let [nodeQueue, nodeIndex, leaf] = info;\n let i =\n lowestKey === undefined\n ? -1\n : indexOf(lowestKey, leaf.keys, 0, comparator) - 1;\n\n if (\n !inclusive &&\n lowestKey !== undefined &&\n // +1 because we did -1 above.\n i + 1 < leaf.keys.length &&\n comparator(leaf.keys[i + 1], lowestKey) === 0\n ) {\n i++;\n }\n\n return new BTreeForwardIterator(nodeQueue, nodeIndex, leaf, i);\n}\n\nfunction valuesFromReversed<K>(\n maxKey: K | undefined,\n root: BNode<K>,\n comparator: Comparator<K>,\n highestKey: K | undefined,\n inclusive: boolean,\n): IterableIterator<K> {\n if (highestKey === undefined) {\n highestKey = maxKey;\n if (highestKey === undefined) {\n return iterator<K>(() => ({done: true, value: undefined}));\n } // collection is empty\n }\n let [nodeQueue, nodeIndex, leaf] =\n findPath(highestKey, root, comparator) ||\n findPath(maxKey, root, comparator)!;\n assert(\n !nodeQueue[0] || leaf === nodeQueue[0][nodeIndex[0]],\n 'BTreeSet: leaf node mismatch in iteration',\n );\n let i = indexOf(highestKey, leaf.keys, 0, comparator);\n if (\n inclusive &&\n i < leaf.keys.length &&\n comparator(leaf.keys[i], highestKey) <= 0\n ) {\n i++;\n }\n\n return new BTreeReverseIterator(nodeQueue, nodeIndex, leaf, i);\n}\n\nfunction findPath<K>(\n key: K | undefined,\n root: BNode<K>,\n comparator: Comparator<K>,\n): [nodeQueue: BNode<K>[][], nodeIndex: number[], leaf: BNode<K>] | undefined {\n let nextNode = root;\n const nodeQueue: BNode<K>[][] = [];\n const nodeIndex: number[] = [];\n\n if (nextNode.isInternal()) {\n for (let d = 0; nextNode.isInternal(); d++) {\n nodeQueue[d] = nextNode.children;\n nodeIndex[d] =\n key === undefined ? 0 : indexOf(key, nextNode.keys, 0, comparator);\n if (nodeIndex[d] >= nodeQueue[d].length) return; // first key > maxKey()\n nextNode = nodeQueue[d][nodeIndex[d]];\n }\n nodeQueue.reverse();\n nodeIndex.reverse();\n }\n return [nodeQueue, nodeIndex, nextNode];\n}\n\nfunction iterator<T>(next: () => IteratorResult<T>): IterableIterator<T> {\n return {\n next,\n [Symbol.iterator]() {\n return this;\n },\n };\n}\n\n/** Leaf node / base class. **************************************************/\nclass BNode<K> {\n // If this is an internal node, _keys[i] is the highest key in children[i].\n keys: K[];\n // True if this node might be within multiple `BTree`s (or have multiple parents).\n // If so, it must be cloned before being mutated to avoid changing an unrelated tree.\n // This is transitive: if it's true, children are also shared even if `isShared!=true`\n // in those children. (Certain operations will propagate isShared=true to children.)\n isShared: true | undefined;\n\n constructor(keys: K[]) {\n this.keys = keys;\n this.isShared = undefined;\n }\n\n isInternal(): this is BNodeInternal<K> {\n return false;\n }\n\n maxKey() {\n // oxlint-disable-next-line typescript/no-non-null-assertion\n return this.keys.at(-1)!;\n }\n\n minKey(): K | undefined {\n return this.keys[0];\n }\n\n clone(): BNode<K> {\n return new BNode<K>(this.keys.slice(0));\n }\n\n get(key: K, tree: BTreeSet<K>): K | undefined {\n const i = indexOf(key, this.keys, -1, tree.comparator);\n return i < 0 ? undefined : this.keys[i];\n }\n\n has(key: K, tree: BTreeSet<K>): boolean {\n const i = indexOf(key, this.keys, -1, tree.comparator);\n return i >= 0 && i < this.keys.length;\n }\n\n set(key: K, tree: BTreeSet<K>): null | BNode<K> {\n let i = indexOf(key, this.keys, -1, tree.comparator);\n if (i < 0) {\n // key does not exist yet\n i = ~i;\n tree.size++;\n\n if (this.keys.length < MAX_NODE_SIZE) {\n this.keys.splice(i, 0, key);\n return null;\n }\n // This leaf node is full and must split\n const newRightSibling = this.splitOffRightSide();\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n let target: BNode<K> = this;\n if (i > this.keys.length) {\n i -= this.keys.length;\n target = newRightSibling;\n }\n // target.#insertInLeaf(i, key);\n target.keys.splice(i, 0, key);\n\n return newRightSibling;\n }\n\n // usually this is a no-op, but some users may wish to edit the key\n this.keys[i] = key;\n return null;\n }\n\n takeFromRight(rhs: BNode<K>) {\n this.keys.push(rhs.keys.shift()!);\n }\n\n takeFromLeft(lhs: BNode<K>) {\n this.keys.unshift(lhs.keys.pop()!);\n }\n\n splitOffRightSide(): BNode<K> {\n const half = this.keys.length >> 1;\n const keys = this.keys.splice(half);\n return new BNode<K>(keys);\n }\n\n delete(key: K, tree: BTreeSet<K>): boolean {\n const cmp = tree.comparator;\n const iLow = indexOf(key, this.keys, -1, cmp);\n const iHigh = iLow + 1;\n\n if (iLow < 0) {\n return false;\n }\n\n const {keys} = this;\n for (let i = iLow; i < iHigh; i++) {\n const key = keys[i];\n\n if (key !== keys[i] || this.isShared === true) {\n throw new Error('BTree illegally changed or cloned in delete');\n }\n\n this.keys.splice(i, 1);\n tree.size--;\n return true;\n }\n\n return false;\n }\n\n mergeSibling(rhs: BNode<K>, _: number) {\n this.keys.push(...rhs.keys);\n }\n}\n\n/** Internal node (non-leaf node) ********************************************/\nclass BNodeInternal<K> extends BNode<K> {\n // Note: conventionally B+ trees have one fewer key than the number of\n // children, but I find it easier to keep the array lengths equal: each\n // keys[i] caches the value of children[i].maxKey().\n children: BNode<K>[];\n\n /**\n * This does not mark `children` as shared, so it is the responsibility of the caller\n * to ensure children are either marked shared, or aren't included in another tree.\n */\n constructor(children: BNode<K>[], keys?: K[]) {\n if (!keys) {\n keys = [];\n for (let i = 0; i < children.length; i++) {\n keys[i] = children[i].maxKey();\n }\n }\n super(keys);\n this.children = children;\n }\n\n isInternal(): this is BNodeInternal<K> {\n return true;\n }\n\n clone(): BNode<K> {\n const children = this.children.slice(0);\n for (let i = 0; i < children.length; i++) {\n children[i].isShared = true;\n }\n return new BNodeInternal<K>(children, this.keys.slice(0));\n }\n\n minKey() {\n return this.children[0].minKey();\n }\n\n get(key: K, tree: BTreeSet<K>): K | undefined {\n const i = indexOf(key, this.keys, 0, tree.comparator);\n const {children} = this;\n return i < children.length ? children[i].get(key, tree) : undefined;\n }\n\n has(key: K, tree: BTreeSet<K>): boolean {\n const i = indexOf(key, this.keys, 0, tree.comparator);\n const {children} = this;\n return i < children.length ? children[i].has(key, tree) : false;\n }\n\n set(key: K, tree: BTreeSet<K>): null | BNode<K> {\n const c = this.children;\n const cmp = tree.comparator;\n let i = Math.min(indexOf(key, this.keys, 0, cmp), c.length - 1);\n let child = c[i];\n\n if (child.isShared) {\n c[i] = child = child.clone();\n }\n if (child.keys.length >= MAX_NODE_SIZE) {\n // child is full; inserting anything else will cause a split.\n // Shifting an item to the left or right sibling may avoid a split.\n // We can do a shift if the adjacent node is not full and if the\n // current key can still be placed in the same node after the shift.\n let other: BNode<K>;\n if (\n i > 0 &&\n (other = c[i - 1]).keys.length < MAX_NODE_SIZE &&\n cmp(child.keys[0], key) < 0\n ) {\n if (other.isShared) {\n c[i - 1] = other = other.clone();\n }\n other.takeFromRight(child);\n this.keys[i - 1] = other.maxKey();\n } else if (\n (other = c[i + 1]) !== undefined &&\n other.keys.length < MAX_NODE_SIZE &&\n cmp(child.maxKey(), key) < 0\n ) {\n if (other.isShared) c[i + 1] = other = other.clone();\n other.takeFromLeft(child);\n this.keys[i] = c[i].maxKey();\n }\n }\n\n const result = child.set(key, tree);\n this.keys[i] = child.maxKey();\n if (result === null) return null;\n\n // The child has split and `result` is a new right child... does it fit?\n if (this.keys.length < MAX_NODE_SIZE) {\n // yes\n this.insert(i + 1, result);\n return null;\n }\n // no, we must split also\n const newRightSibling = this.splitOffRightSide();\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n let target: BNodeInternal<K> = this;\n if (cmp(result.maxKey(), this.maxKey()) > 0) {\n target = newRightSibling;\n i -= this.keys.length;\n }\n target.insert(i + 1, result);\n return newRightSibling;\n }\n\n /**\n * Inserts `child` at index `i`.\n * This does not mark `child` as shared, so it is the responsibility of the caller\n * to ensure that either child is marked shared, or it is not included in another tree.\n */\n insert(i: number, child: BNode<K>) {\n this.children.splice(i, 0, child);\n this.keys.splice(i, 0, child.maxKey());\n }\n\n /**\n * Split this node.\n * Modifies this to remove the second half of the items, returning a separate node containing them.\n */\n splitOffRightSide() {\n const half = this.children.length >> 1;\n return new BNodeInternal<K>(\n this.children.splice(half),\n this.keys.splice(half),\n );\n }\n\n takeFromRight(rhs: BNode<K>) {\n this.keys.push(rhs.keys.shift()!);\n this.children.push((rhs as BNodeInternal<K>).children.shift()!);\n }\n\n takeFromLeft(lhs: BNode<K>) {\n this.keys.unshift(lhs.keys.pop()!);\n this.children.unshift((lhs as BNodeInternal<K>).children.pop()!);\n }\n\n delete(key: K, tree: BTreeSet<K>): boolean {\n const cmp = tree.comparator;\n const {keys} = this;\n const {children} = this;\n let iLow = indexOf(key, this.keys, 0, cmp);\n let i = iLow;\n const iHigh = Math.min(iLow, keys.length - 1);\n if (i <= iHigh) {\n try {\n if (children[i].isShared) {\n children[i] = children[i].clone();\n }\n const result = children[i].delete(key, tree);\n // Note: if children[i] is empty then keys[i]=undefined.\n // This is an invalid state, but it is fixed below.\n keys[i] = children[i].maxKey();\n return result;\n } finally {\n // Deletions may have occurred, so look for opportunities to merge nodes.\n const half = MAX_NODE_SIZE >> 1;\n if (iLow > 0) iLow--;\n for (i = iHigh; i >= iLow; i--) {\n if (children[i].keys.length <= half) {\n if (children[i].keys.length !== 0) {\n this.tryMerge(i, MAX_NODE_SIZE);\n } else {\n // child is empty! delete it!\n keys.splice(i, 1);\n children.splice(i, 1);\n }\n }\n }\n }\n }\n return false;\n }\n\n /** Merges child i with child i+1 if their combined size is not too large */\n tryMerge(i: number, maxSize: number): boolean {\n const {children} = this;\n if (i >= 0 && i + 1 < children.length) {\n if (children[i].keys.length + children[i + 1].keys.length <= maxSize) {\n if (\n children[i].isShared\n ) // cloned already UNLESS i is outside scan range\n {\n children[i] = children[i].clone();\n }\n children[i].mergeSibling(children[i + 1], maxSize);\n children.splice(i + 1, 1);\n this.keys.splice(i + 1, 1);\n this.keys[i] = children[i].maxKey();\n return true;\n }\n }\n return false;\n }\n\n /**\n * Move children from `rhs` into this.\n * `rhs` must be part of this tree, and be removed from it after this call\n * (otherwise isShared for its children could be incorrect).\n */\n mergeSibling(rhs: BNode<K>, maxNodeSize: number) {\n // assert !this.isShared;\n const oldLength = this.keys.length;\n this.keys.push(...rhs.keys);\n const rhsChildren = (rhs as unknown as BNodeInternal<K>).children;\n this.children.push(...rhsChildren);\n\n if (rhs.isShared && !this.isShared) {\n // All children of a shared node are implicitly shared, and since their new\n // parent is not shared, they must now be explicitly marked as shared.\n for (let i = 0; i < rhsChildren.length; i++) {\n rhsChildren[i].isShared = true;\n }\n }\n\n // If our children are themselves almost empty due to a mass-delete,\n // they may need to be merged too (but only the oldLength-1 and its\n // right sibling should need this).\n this.tryMerge(oldLength - 1, maxNodeSize);\n }\n}\n\n// If key not found, returns i^failXor where i is the insertion index.\n// Callers that don't care whether there was a match will set failXor=0.\nfunction indexOf<K>(\n key: K,\n keys: K[],\n failXor: number,\n comparator: Comparator<K>,\n): number {\n let lo = 0;\n let hi = keys.length;\n let mid = hi >> 1;\n while (lo < hi) {\n const c = comparator(keys[mid], key);\n if (c < 0) {\n lo = mid + 1;\n } else if (c > 0) {\n // key < keys[mid]\n hi = mid;\n } else if (c === 0) {\n return mid;\n } else {\n // c is NaN or otherwise invalid\n if (key === key) {\n // at least the search key is not NaN\n return keys.length;\n }\n throw new Error('NaN was used as a key');\n }\n mid = (lo + hi) >> 1;\n }\n return mid ^ failXor;\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nconst emptyLeaf = new BNode<any>([]);\nemptyLeaf.isShared = true;\n"],"mappings":"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"}
1
+ {"version":3,"file":"btree-set.js","names":[],"sources":["../../../../shared/src/btree-set.ts"],"sourcesContent":["import {assert} from './asserts.ts';\n\nconst MAX_NODE_SIZE = 32;\n\n/**\n * How many arrays `toArray` hands `concat` at once. Engines cap how many\n * arguments a call may spread, so the join is chunked rather than passing one\n * argument per leaf.\n */\nconst CONCAT_CHUNK = 1024;\n\nfunction concatAll<K>(arrays: K[][]): K[] {\n // `concat` on purpose, not `[...a, ...b]`: this is the one place where the\n // difference is the point. `concat` copies the arrays in native code, which\n // on Hermes is 10x faster than getting the same keys out through the tree's\n // iterator, and 3x faster than a per-element JS loop (see `toArray`).\n // oxlint-disable-next-line e18e/prefer-spread-syntax\n return ([] as K[]).concat(...arrays);\n}\n\ntype Comparator<K> = (a: K, b: K) => number;\n\nexport class BTreeSet<K> {\n #root: BNode<K> = emptyLeaf as BNode<K>;\n size: number = 0;\n\n readonly comparator: Comparator<K>;\n\n constructor(comparator: Comparator<K>, entries?: IterableIterator<K>) {\n this.comparator = comparator;\n if (entries) {\n for (const key of entries) {\n this.add(key);\n }\n }\n }\n\n /** Releases the tree so that its size is 0. */\n clear() {\n this.#root = emptyLeaf as BNode<K>;\n this.size = 0;\n }\n\n clone() {\n this.#root.isShared = true;\n const ret = new BTreeSet<K>(this.comparator);\n ret.#root = this.#root;\n ret.size = this.size;\n return ret;\n }\n\n get(key: K): K | undefined {\n return this.#root.get(key, this);\n }\n\n add(key: K): this {\n if (this.#root.isShared) this.#root = this.#root.clone();\n const result = this.#root.set(key, this);\n if (result === null) return this;\n // Root node has split, so create a new root node.\n this.#root = new BNodeInternal<K>([this.#root, result]);\n return this;\n }\n\n /**\n * Returns true if the key exists in the B+ tree, false if not.\n * Use get() for best performance; use has() if you need to\n * distinguish between \"undefined value\" and \"key not present\".\n * @param key Key to detect\n * @description Computational complexity: O(log size)\n */\n has(key: K): boolean {\n return this.#root.has(key, this);\n }\n\n /**\n * Removes a single key-value pair from the B+ tree.\n * @param key Key to find\n * @returns true if a pair was found and removed, false otherwise.\n * @description Computational complexity: O(log size)\n */\n delete(key: K): boolean {\n return this.#delete(key);\n }\n\n #delete(key: K): boolean {\n let root = this.#root;\n if (root.isShared) {\n this.#root = root = root.clone();\n }\n try {\n return root.delete(key, this);\n } finally {\n let isShared;\n while (root.keys.length <= 1 && root.isInternal()) {\n isShared ||= root.isShared;\n this.#root = root =\n root.keys.length === 0 ? emptyLeaf : root.children[0];\n }\n // If any ancestor of the new root was shared, the new root must also be shared\n if (isShared) {\n root.isShared = true;\n }\n }\n }\n\n keys(): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, undefined, true);\n }\n\n values(): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, undefined, true);\n }\n\n valuesFrom(lowestKey?: K, inclusive: boolean = true): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, lowestKey, inclusive);\n }\n\n valuesReversed(): IterableIterator<K> {\n return valuesFromReversed(\n this.#maxKey(),\n this.#root,\n this.comparator,\n undefined,\n true,\n );\n }\n\n valuesFromReversed(\n highestKey?: K,\n inclusive: boolean = true,\n ): IterableIterator<K> {\n return valuesFromReversed(\n this.#maxKey(),\n this.#root,\n this.comparator,\n highestKey,\n inclusive,\n );\n }\n\n /** Gets the highest key in the tree. Complexity: O(1) */\n #maxKey(): K | undefined {\n return this.#root.maxKey();\n }\n\n [Symbol.iterator](): IterableIterator<K> {\n return this.keys();\n }\n\n /**\n * Returns every key, in order, as a fresh array the caller owns (sorting it\n * in place is safe).\n *\n * This is the inverse of `fromSorted`: it collects the leaves' `keys` arrays\n * and joins them with `concat`, instead of yielding one key at a time. Every\n * key the iterator yields pays for a `next()` call that walks a node queue\n * and allocates a result object, and on an interpreter that is not something\n * a JS loop can win back -- the work has to happen in native code.\n *\n * Measured on an Android emulator (Hermes, release), 90,000 rows per call:\n * 5.76ms through the iterator, 1.73ms for a per-element JS copy loop into a\n * pre-sized array, 0.56ms for this. So a JS loop buys 3.3x and native copying\n * 10x; at 30,000 rows, where the join is a single round, it is 20x. V8 JITs\n * such a loop to near-memcpy, so the choice barely registers there and is\n * worth 3x on a phone.\n *\n * Complexity: O(size). Nothing collected is mutated, so shared/cloned nodes\n * are safe, and the arrays are copied rather than handed out.\n */\n toArray(): K[] {\n const root = this.#root;\n // The common small case: one native copy, and no leaf collection at all.\n if (!root.isInternal()) {\n return root.keys.slice();\n }\n const leaves: K[][] = [];\n root.collectLeafKeys(leaves);\n // `concat` takes the leaves as arguments, so they are joined in chunks to\n // keep the argument count bounded however large the tree is. Each round\n // copies the data once, and with 32 keys per leaf one round covers 32k rows\n // and two cover 33M, so this is one or two passes in practice.\n let level = leaves;\n while (level.length > 1) {\n const next: K[][] = [];\n for (let i = 0; i < level.length; i += CONCAT_CHUNK) {\n next.push(concatAll(level.slice(i, i + CONCAT_CHUNK)));\n }\n level = next;\n }\n // An internal node always has at least one child, so `level` holds exactly\n // one array here; the fallback is for a tree in no state this class builds.\n return level[0] ?? [];\n }\n\n /**\n * Builds a BTreeSet from a pre-sorted iterable in O(N) by constructing\n * the tree bottom-up. The caller must ensure entries are sorted by\n * `comparator`; violating this produces an invalid tree.\n */\n static fromSorted<K>(\n comparator: Comparator<K>,\n sortedEntries: Iterable<K>,\n ): BTreeSet<K> {\n const tree = new BTreeSet<K>(comparator);\n\n // Stream through entries, flushing a leaf node each time we fill MAX_NODE_SIZE keys.\n const leaves: BNode<K>[] = [];\n let currentKeys: K[] = [];\n let size = 0;\n\n for (const key of sortedEntries) {\n currentKeys.push(key);\n if (currentKeys.length === MAX_NODE_SIZE) {\n leaves.push(new BNode<K>(currentKeys));\n currentKeys = [];\n }\n size++;\n }\n\n if (currentKeys.length > 0) {\n leaves.push(new BNode<K>(currentKeys));\n }\n\n if (leaves.length === 0) return tree;\n\n tree.size = size;\n\n if (leaves.length === 1) {\n tree.#root = leaves[0];\n return tree;\n }\n\n // Build internal nodes bottom-up until a single root remains.\n let currentLevel: BNode<K>[] = leaves;\n while (currentLevel.length > 1) {\n const nextLevel: BNodeInternal<K>[] = [];\n for (let i = 0; i < currentLevel.length; i += MAX_NODE_SIZE) {\n nextLevel.push(\n new BNodeInternal<K>(currentLevel.slice(i, i + MAX_NODE_SIZE)),\n );\n }\n currentLevel = nextLevel;\n }\n\n tree.#root = currentLevel[0];\n return tree;\n }\n}\n\nclass BTreeForwardIterator<K> implements IterableIterator<K> {\n readonly #nodeQueue: BNode<K>[][];\n readonly #nodeIndex: number[];\n #leaf: BNode<K>;\n #i: number;\n\n constructor(\n nodeQueue: BNode<K>[][],\n nodeIndex: number[],\n leaf: BNode<K>,\n startI: number,\n ) {\n this.#nodeQueue = nodeQueue;\n this.#nodeIndex = nodeIndex;\n this.#leaf = leaf;\n this.#i = startI;\n }\n\n next(): IteratorResult<K> {\n for (;;) {\n if (++this.#i < this.#leaf.keys.length) {\n return {done: false, value: this.#leaf.keys[this.#i]};\n }\n\n let level = -1;\n for (;;) {\n if (++level >= this.#nodeQueue.length) {\n return {done: true, value: undefined as unknown as K};\n }\n if (++this.#nodeIndex[level] < this.#nodeQueue[level].length) {\n break;\n }\n }\n for (; level > 0; level--) {\n this.#nodeQueue[level - 1] = (\n this.#nodeQueue[level][this.#nodeIndex[level]] as BNodeInternal<K>\n ).children;\n this.#nodeIndex[level - 1] = 0;\n }\n this.#leaf = this.#nodeQueue[0][this.#nodeIndex[0]];\n this.#i = -1;\n }\n }\n\n [Symbol.iterator]() {\n return this;\n }\n}\n\nclass BTreeReverseIterator<K> implements IterableIterator<K> {\n readonly #nodeQueue: BNode<K>[][];\n readonly #nodeIndex: number[];\n #leaf: BNode<K>;\n #i: number;\n\n constructor(\n nodeQueue: BNode<K>[][],\n nodeIndex: number[],\n leaf: BNode<K>,\n startI: number,\n ) {\n this.#nodeQueue = nodeQueue;\n this.#nodeIndex = nodeIndex;\n this.#leaf = leaf;\n this.#i = startI;\n }\n\n next(): IteratorResult<K> {\n for (;;) {\n if (--this.#i >= 0) {\n return {done: false, value: this.#leaf.keys[this.#i]};\n }\n\n let level;\n // Advance to the next leaf node\n for (level = -1; ;) {\n if (++level >= this.#nodeQueue.length) {\n return {done: true, value: undefined as unknown as K};\n }\n if (--this.#nodeIndex[level] >= 0) {\n break;\n }\n }\n for (; level > 0; level--) {\n this.#nodeQueue[level - 1] = (\n this.#nodeQueue[level][this.#nodeIndex[level]] as BNodeInternal<K>\n ).children;\n this.#nodeIndex[level - 1] = this.#nodeQueue[level - 1].length - 1;\n }\n this.#leaf = this.#nodeQueue[0][this.#nodeIndex[0]];\n this.#i = this.#leaf.keys.length;\n }\n }\n\n [Symbol.iterator]() {\n return this;\n }\n}\n\nfunction valuesFrom<K>(\n root: BNode<K>,\n comparator: Comparator<K>,\n lowestKey: K | undefined,\n inclusive: boolean,\n): IterableIterator<K> {\n const info = findPath(lowestKey, root, comparator);\n if (info === undefined) {\n return iterator<K>(() => ({done: true, value: undefined}));\n }\n\n let [nodeQueue, nodeIndex, leaf] = info;\n let i =\n lowestKey === undefined\n ? -1\n : indexOf(lowestKey, leaf.keys, 0, comparator) - 1;\n\n if (\n !inclusive &&\n lowestKey !== undefined &&\n // +1 because we did -1 above.\n i + 1 < leaf.keys.length &&\n comparator(leaf.keys[i + 1], lowestKey) === 0\n ) {\n i++;\n }\n\n return new BTreeForwardIterator(nodeQueue, nodeIndex, leaf, i);\n}\n\nfunction valuesFromReversed<K>(\n maxKey: K | undefined,\n root: BNode<K>,\n comparator: Comparator<K>,\n highestKey: K | undefined,\n inclusive: boolean,\n): IterableIterator<K> {\n if (highestKey === undefined) {\n highestKey = maxKey;\n if (highestKey === undefined) {\n return iterator<K>(() => ({done: true, value: undefined}));\n } // collection is empty\n }\n let [nodeQueue, nodeIndex, leaf] =\n findPath(highestKey, root, comparator) ||\n findPath(maxKey, root, comparator)!;\n assert(\n !nodeQueue[0] || leaf === nodeQueue[0][nodeIndex[0]],\n 'BTreeSet: leaf node mismatch in iteration',\n );\n let i = indexOf(highestKey, leaf.keys, 0, comparator);\n if (\n inclusive &&\n i < leaf.keys.length &&\n comparator(leaf.keys[i], highestKey) <= 0\n ) {\n i++;\n }\n\n return new BTreeReverseIterator(nodeQueue, nodeIndex, leaf, i);\n}\n\nfunction findPath<K>(\n key: K | undefined,\n root: BNode<K>,\n comparator: Comparator<K>,\n): [nodeQueue: BNode<K>[][], nodeIndex: number[], leaf: BNode<K>] | undefined {\n let nextNode = root;\n const nodeQueue: BNode<K>[][] = [];\n const nodeIndex: number[] = [];\n\n if (nextNode.isInternal()) {\n for (let d = 0; nextNode.isInternal(); d++) {\n nodeQueue[d] = nextNode.children;\n nodeIndex[d] =\n key === undefined ? 0 : indexOf(key, nextNode.keys, 0, comparator);\n if (nodeIndex[d] >= nodeQueue[d].length) return; // first key > maxKey()\n nextNode = nodeQueue[d][nodeIndex[d]];\n }\n nodeQueue.reverse();\n nodeIndex.reverse();\n }\n return [nodeQueue, nodeIndex, nextNode];\n}\n\nfunction iterator<T>(next: () => IteratorResult<T>): IterableIterator<T> {\n return {\n next,\n [Symbol.iterator]() {\n return this;\n },\n };\n}\n\n/** Leaf node / base class. **************************************************/\nclass BNode<K> {\n // If this is an internal node, _keys[i] is the highest key in children[i].\n keys: K[];\n // True if this node might be within multiple `BTree`s (or have multiple parents).\n // If so, it must be cloned before being mutated to avoid changing an unrelated tree.\n // This is transitive: if it's true, children are also shared even if `isShared!=true`\n // in those children. (Certain operations will propagate isShared=true to children.)\n isShared: true | undefined;\n\n constructor(keys: K[]) {\n this.keys = keys;\n this.isShared = undefined;\n }\n\n isInternal(): this is BNodeInternal<K> {\n return false;\n }\n\n maxKey() {\n // oxlint-disable-next-line typescript/no-non-null-assertion\n return this.keys.at(-1)!;\n }\n\n minKey(): K | undefined {\n return this.keys[0];\n }\n\n clone(): BNode<K> {\n return new BNode<K>(this.keys.slice(0));\n }\n\n /**\n * Appends this node's `keys` arrays, in order, to `out`. Read-only: the\n * arrays are not copied here and must not be mutated by the caller, which is\n * why `toArray` only ever passes them to `concat`.\n */\n collectLeafKeys(out: K[][]): void {\n out.push(this.keys);\n }\n\n get(key: K, tree: BTreeSet<K>): K | undefined {\n const i = indexOf(key, this.keys, -1, tree.comparator);\n return i < 0 ? undefined : this.keys[i];\n }\n\n has(key: K, tree: BTreeSet<K>): boolean {\n const i = indexOf(key, this.keys, -1, tree.comparator);\n return i >= 0 && i < this.keys.length;\n }\n\n set(key: K, tree: BTreeSet<K>): null | BNode<K> {\n let i = indexOf(key, this.keys, -1, tree.comparator);\n if (i < 0) {\n // key does not exist yet\n i = ~i;\n tree.size++;\n\n if (this.keys.length < MAX_NODE_SIZE) {\n this.keys.splice(i, 0, key);\n return null;\n }\n // This leaf node is full and must split\n const newRightSibling = this.splitOffRightSide();\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n let target: BNode<K> = this;\n if (i > this.keys.length) {\n i -= this.keys.length;\n target = newRightSibling;\n }\n // target.#insertInLeaf(i, key);\n target.keys.splice(i, 0, key);\n\n return newRightSibling;\n }\n\n // usually this is a no-op, but some users may wish to edit the key\n this.keys[i] = key;\n return null;\n }\n\n takeFromRight(rhs: BNode<K>) {\n this.keys.push(rhs.keys.shift()!);\n }\n\n takeFromLeft(lhs: BNode<K>) {\n this.keys.unshift(lhs.keys.pop()!);\n }\n\n splitOffRightSide(): BNode<K> {\n const half = this.keys.length >> 1;\n const keys = this.keys.splice(half);\n return new BNode<K>(keys);\n }\n\n delete(key: K, tree: BTreeSet<K>): boolean {\n const cmp = tree.comparator;\n const iLow = indexOf(key, this.keys, -1, cmp);\n const iHigh = iLow + 1;\n\n if (iLow < 0) {\n return false;\n }\n\n const {keys} = this;\n for (let i = iLow; i < iHigh; i++) {\n const key = keys[i];\n\n if (key !== keys[i] || this.isShared === true) {\n throw new Error('BTree illegally changed or cloned in delete');\n }\n\n this.keys.splice(i, 1);\n tree.size--;\n return true;\n }\n\n return false;\n }\n\n mergeSibling(rhs: BNode<K>, _: number) {\n this.keys.push(...rhs.keys);\n }\n}\n\n/** Internal node (non-leaf node) ********************************************/\nclass BNodeInternal<K> extends BNode<K> {\n // Note: conventionally B+ trees have one fewer key than the number of\n // children, but I find it easier to keep the array lengths equal: each\n // keys[i] caches the value of children[i].maxKey().\n children: BNode<K>[];\n\n /**\n * This does not mark `children` as shared, so it is the responsibility of the caller\n * to ensure children are either marked shared, or aren't included in another tree.\n */\n constructor(children: BNode<K>[], keys?: K[]) {\n if (!keys) {\n keys = [];\n for (let i = 0; i < children.length; i++) {\n keys[i] = children[i].maxKey();\n }\n }\n super(keys);\n this.children = children;\n }\n\n isInternal(): this is BNodeInternal<K> {\n return true;\n }\n\n clone(): BNode<K> {\n const children = this.children.slice(0);\n for (let i = 0; i < children.length; i++) {\n children[i].isShared = true;\n }\n return new BNodeInternal<K>(children, this.keys.slice(0));\n }\n\n minKey() {\n return this.children[0].minKey();\n }\n\n collectLeafKeys(out: K[][]): void {\n // An internal node's `keys` cache its children's max keys, so the keys live\n // in the leaves. Recursion depth is the tree height, log_32(size).\n const {children} = this;\n for (let i = 0; i < children.length; i++) {\n children[i].collectLeafKeys(out);\n }\n }\n\n get(key: K, tree: BTreeSet<K>): K | undefined {\n const i = indexOf(key, this.keys, 0, tree.comparator);\n const {children} = this;\n return i < children.length ? children[i].get(key, tree) : undefined;\n }\n\n has(key: K, tree: BTreeSet<K>): boolean {\n const i = indexOf(key, this.keys, 0, tree.comparator);\n const {children} = this;\n return i < children.length ? children[i].has(key, tree) : false;\n }\n\n set(key: K, tree: BTreeSet<K>): null | BNode<K> {\n const c = this.children;\n const cmp = tree.comparator;\n let i = Math.min(indexOf(key, this.keys, 0, cmp), c.length - 1);\n let child = c[i];\n\n if (child.isShared) {\n c[i] = child = child.clone();\n }\n if (child.keys.length >= MAX_NODE_SIZE) {\n // child is full; inserting anything else will cause a split.\n // Shifting an item to the left or right sibling may avoid a split.\n // We can do a shift if the adjacent node is not full and if the\n // current key can still be placed in the same node after the shift.\n let other: BNode<K>;\n if (\n i > 0 &&\n (other = c[i - 1]).keys.length < MAX_NODE_SIZE &&\n cmp(child.keys[0], key) < 0\n ) {\n if (other.isShared) {\n c[i - 1] = other = other.clone();\n }\n other.takeFromRight(child);\n this.keys[i - 1] = other.maxKey();\n } else if (\n (other = c[i + 1]) !== undefined &&\n other.keys.length < MAX_NODE_SIZE &&\n cmp(child.maxKey(), key) < 0\n ) {\n if (other.isShared) c[i + 1] = other = other.clone();\n other.takeFromLeft(child);\n this.keys[i] = c[i].maxKey();\n }\n }\n\n const result = child.set(key, tree);\n this.keys[i] = child.maxKey();\n if (result === null) return null;\n\n // The child has split and `result` is a new right child... does it fit?\n if (this.keys.length < MAX_NODE_SIZE) {\n // yes\n this.insert(i + 1, result);\n return null;\n }\n // no, we must split also\n const newRightSibling = this.splitOffRightSide();\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n let target: BNodeInternal<K> = this;\n if (cmp(result.maxKey(), this.maxKey()) > 0) {\n target = newRightSibling;\n i -= this.keys.length;\n }\n target.insert(i + 1, result);\n return newRightSibling;\n }\n\n /**\n * Inserts `child` at index `i`.\n * This does not mark `child` as shared, so it is the responsibility of the caller\n * to ensure that either child is marked shared, or it is not included in another tree.\n */\n insert(i: number, child: BNode<K>) {\n this.children.splice(i, 0, child);\n this.keys.splice(i, 0, child.maxKey());\n }\n\n /**\n * Split this node.\n * Modifies this to remove the second half of the items, returning a separate node containing them.\n */\n splitOffRightSide() {\n const half = this.children.length >> 1;\n return new BNodeInternal<K>(\n this.children.splice(half),\n this.keys.splice(half),\n );\n }\n\n takeFromRight(rhs: BNode<K>) {\n this.keys.push(rhs.keys.shift()!);\n this.children.push((rhs as BNodeInternal<K>).children.shift()!);\n }\n\n takeFromLeft(lhs: BNode<K>) {\n this.keys.unshift(lhs.keys.pop()!);\n this.children.unshift((lhs as BNodeInternal<K>).children.pop()!);\n }\n\n delete(key: K, tree: BTreeSet<K>): boolean {\n const cmp = tree.comparator;\n const {keys} = this;\n const {children} = this;\n let iLow = indexOf(key, this.keys, 0, cmp);\n let i = iLow;\n const iHigh = Math.min(iLow, keys.length - 1);\n if (i <= iHigh) {\n try {\n if (children[i].isShared) {\n children[i] = children[i].clone();\n }\n const result = children[i].delete(key, tree);\n // Note: if children[i] is empty then keys[i]=undefined.\n // This is an invalid state, but it is fixed below.\n keys[i] = children[i].maxKey();\n return result;\n } finally {\n // Deletions may have occurred, so look for opportunities to merge nodes.\n const half = MAX_NODE_SIZE >> 1;\n if (iLow > 0) iLow--;\n for (i = iHigh; i >= iLow; i--) {\n if (children[i].keys.length <= half) {\n if (children[i].keys.length !== 0) {\n this.tryMerge(i, MAX_NODE_SIZE);\n } else {\n // child is empty! delete it!\n keys.splice(i, 1);\n children.splice(i, 1);\n }\n }\n }\n }\n }\n return false;\n }\n\n /** Merges child i with child i+1 if their combined size is not too large */\n tryMerge(i: number, maxSize: number): boolean {\n const {children} = this;\n if (i >= 0 && i + 1 < children.length) {\n if (children[i].keys.length + children[i + 1].keys.length <= maxSize) {\n if (\n children[i].isShared\n ) // cloned already UNLESS i is outside scan range\n {\n children[i] = children[i].clone();\n }\n children[i].mergeSibling(children[i + 1], maxSize);\n children.splice(i + 1, 1);\n this.keys.splice(i + 1, 1);\n this.keys[i] = children[i].maxKey();\n return true;\n }\n }\n return false;\n }\n\n /**\n * Move children from `rhs` into this.\n * `rhs` must be part of this tree, and be removed from it after this call\n * (otherwise isShared for its children could be incorrect).\n */\n mergeSibling(rhs: BNode<K>, maxNodeSize: number) {\n // assert !this.isShared;\n const oldLength = this.keys.length;\n this.keys.push(...rhs.keys);\n const rhsChildren = (rhs as unknown as BNodeInternal<K>).children;\n this.children.push(...rhsChildren);\n\n if (rhs.isShared && !this.isShared) {\n // All children of a shared node are implicitly shared, and since their new\n // parent is not shared, they must now be explicitly marked as shared.\n for (let i = 0; i < rhsChildren.length; i++) {\n rhsChildren[i].isShared = true;\n }\n }\n\n // If our children are themselves almost empty due to a mass-delete,\n // they may need to be merged too (but only the oldLength-1 and its\n // right sibling should need this).\n this.tryMerge(oldLength - 1, maxNodeSize);\n }\n}\n\n// If key not found, returns i^failXor where i is the insertion index.\n// Callers that don't care whether there was a match will set failXor=0.\nfunction indexOf<K>(\n key: K,\n keys: K[],\n failXor: number,\n comparator: Comparator<K>,\n): number {\n let lo = 0;\n let hi = keys.length;\n let mid = hi >> 1;\n while (lo < hi) {\n const c = comparator(keys[mid], key);\n if (c < 0) {\n lo = mid + 1;\n } else if (c > 0) {\n // key < keys[mid]\n hi = mid;\n } else if (c === 0) {\n return mid;\n } else {\n // c is NaN or otherwise invalid\n if (key === key) {\n // at least the search key is not NaN\n return keys.length;\n }\n throw new Error('NaN was used as a key');\n }\n mid = (lo + hi) >> 1;\n }\n return mid ^ failXor;\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nconst emptyLeaf = new BNode<any>([]);\nemptyLeaf.isShared = true;\n"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ //#region ../shared/src/headers.ts
2
+ var BEARER_PREFIX = "Bearer ";
3
+ var BEARER_PATTERN = /^bearer +(.*)$/i;
4
+ /**
5
+ * Returns the token of an `Authorization: Bearer <token>` header value, or
6
+ * `undefined` if there is no value or it uses another scheme.
7
+ */
8
+ function getBearerToken(authorization) {
9
+ return authorization === void 0 ? void 0 : BEARER_PATTERN.exec(authorization)?.[1];
10
+ }
11
+ /**
12
+ * Formats `token` as the value of an `Authorization: Bearer <token>` header.
13
+ */
14
+ function bearerAuthorization(token) {
15
+ return BEARER_PREFIX + token;
16
+ }
17
+ //#endregion
18
+ export { bearerAuthorization, getBearerToken };
19
+
20
+ //# sourceMappingURL=headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.js","names":[],"sources":["../../../../shared/src/headers.ts"],"sourcesContent":["export function encodeHeaderValue(value: string): string {\n // encodeURIComponent escapes the following chars which are allowed\n // in header values.\n // : ; , / \" ? { } [ ] @ < > = + # $ & ` | ^ space and %\n // Unescape all of them expect %, to make the encoded value smaller and more\n // readable. Do not unescape % as that would break decoding of the\n // percent decoding done by encodeURIComponent.\n return encodeURIComponent(value).replace(\n /%(3A|3B|2C|2F|22|3F|7B|7D|5B|5D|40|3C|3E|3D|2B|23|24|26|60|7C|5E|20)/g,\n (_, hex) => String.fromCharCode(parseInt(hex, 16)),\n );\n}\n\nexport function decodeHeaderValue(encoded: string): string {\n return decodeURIComponent(encoded);\n}\n\nconst BEARER_PREFIX = 'Bearer ';\n\n// RFC 9110: the scheme is case-insensitive and is followed by one or more\n// spaces.\nconst BEARER_PATTERN = /^bearer +(.*)$/i;\n\n/**\n * Returns the token of an `Authorization: Bearer <token>` header value, or\n * `undefined` if there is no value or it uses another scheme.\n */\nexport function getBearerToken(\n authorization: string | undefined,\n): string | undefined {\n return authorization === undefined\n ? undefined\n : BEARER_PATTERN.exec(authorization)?.[1];\n}\n\n/**\n * Formats `token` as the value of an `Authorization: Bearer <token>` header.\n */\nexport function bearerAuthorization(token: string): string {\n return BEARER_PREFIX + token;\n}\n"],"mappings":";AAiBA,IAAM,gBAAgB;AAItB,IAAM,iBAAiB;;;;;AAMvB,SAAgB,eACd,eACoB;CACpB,OAAO,kBAAkB,KAAA,IACrB,KAAA,IACA,eAAe,KAAK,aAAa,CAAC,GAAG;AAC3C;;;;AAKA,SAAgB,oBAAoB,OAAuB;CACzD,OAAO,gBAAgB;AACzB"}