@rocicorp/zero 1.11.0-canary.41 → 1.11.0-canary.43

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 (125) hide show
  1. package/out/replicache/src/deleted-clients.d.ts +2 -2
  2. package/out/replicache/src/deleted-clients.js +2 -2
  3. package/out/replicache/src/deleted-clients.js.map +1 -1
  4. package/out/replicache/src/persist/client-group-gc.d.ts +1 -1
  5. package/out/replicache/src/persist/client-group-gc.js +1 -1
  6. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  7. package/out/replicache/src/replicache-options.d.ts +1 -1
  8. package/out/shared/src/browser-env.d.ts +1 -1
  9. package/out/shared/src/browser-env.js.map +1 -1
  10. package/out/shared/src/set-utils.d.ts +1 -1
  11. package/out/shared/src/set-utils.js +1 -1
  12. package/out/shared/src/set-utils.js.map +1 -1
  13. package/out/zero/package.js +1 -1
  14. package/out/zero/package.js.map +1 -1
  15. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  16. package/out/zero-cache/src/server/change-streamer.js +2 -0
  17. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  18. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  19. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +4 -2
  20. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  21. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  22. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +17 -6
  23. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  24. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +3 -1
  25. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  26. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +4 -1
  27. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  28. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +3 -3
  29. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  30. package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts +4 -4
  31. package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts.map +1 -1
  32. package/out/zero-cache/src/services/change-streamer/drain-tracker.js +4 -8
  33. package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
  34. package/out/zero-cache/src/services/http-service.d.ts +6 -0
  35. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  36. package/out/zero-cache/src/services/http-service.js +11 -0
  37. package/out/zero-cache/src/services/http-service.js.map +1 -1
  38. package/out/zero-cache/src/services/life-cycle.d.ts +3 -2
  39. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  40. package/out/zero-cache/src/services/life-cycle.js +17 -4
  41. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  42. package/out/zero-cache/src/services/replicator/drain-handler.d.ts +8 -0
  43. package/out/zero-cache/src/services/replicator/drain-handler.d.ts.map +1 -1
  44. package/out/zero-cache/src/services/replicator/drain-handler.js +38 -4
  45. package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
  46. package/out/zero-cache/src/types/pg-patch.d.ts +21 -0
  47. package/out/zero-cache/src/types/pg-patch.d.ts.map +1 -0
  48. package/out/zero-cache/src/types/pg-patch.js +29 -0
  49. package/out/zero-cache/src/types/pg-patch.js.map +1 -0
  50. package/out/zero-client/src/client/connection-manager.js +1 -1
  51. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  52. package/out/zero-client/src/client/custom.d.ts +1 -1
  53. package/out/zero-client/src/client/custom.js +1 -1
  54. package/out/zero-client/src/client/custom.js.map +1 -1
  55. package/out/zero-client/src/client/options.d.ts +6 -6
  56. package/out/zero-client/src/client/options.js.map +1 -1
  57. package/out/zero-client/src/client/version.js +1 -1
  58. package/out/zero-client/src/client/zero.d.ts +1 -1
  59. package/out/zero-client/src/client/zero.js +1 -1
  60. package/out/zero-client/src/client/zero.js.map +1 -1
  61. package/out/zero-protocol/src/inspect-up.d.ts +3 -3
  62. package/out/zero-protocol/src/inspect-up.d.ts.map +1 -1
  63. package/out/zero-protocol/src/inspect-up.js +2 -2
  64. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  65. package/out/zero-react/src/use-zero-online.d.ts +1 -1
  66. package/out/zero-react/src/use-zero-online.js +1 -1
  67. package/out/zero-react/src/use-zero-online.js.map +1 -1
  68. package/out/zero-react/src/zero-provider.d.ts +1 -1
  69. package/out/zero-react/src/zero-provider.js +1 -1
  70. package/out/zero-react/src/zero-provider.js.map +1 -1
  71. package/out/zero-server/src/closable-db-transaction.d.ts +1 -1
  72. package/out/zero-server/src/closable-db-transaction.js +1 -1
  73. package/out/zero-server/src/closable-db-transaction.js.map +1 -1
  74. package/out/zero-server/src/custom.d.ts +1 -1
  75. package/out/zero-server/src/custom.js +1 -1
  76. package/out/zero-server/src/custom.js.map +1 -1
  77. package/out/zero-server/src/process-mutations.d.ts +3 -3
  78. package/out/zero-server/src/process-mutations.js +1 -1
  79. package/out/zero-server/src/process-mutations.js.map +1 -1
  80. package/out/zero-server/src/push-processor.d.ts +1 -1
  81. package/out/zero-server/src/push-processor.js +1 -1
  82. package/out/zero-server/src/push-processor.js.map +1 -1
  83. package/out/zero-server/src/queries/process-queries.d.ts +3 -3
  84. package/out/zero-server/src/queries/process-queries.js +2 -2
  85. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  86. package/out/zero-server/src/schema.d.ts +1 -1
  87. package/out/zero-server/src/schema.js +1 -1
  88. package/out/zero-server/src/schema.js.map +1 -1
  89. package/out/zero-server/src/zql-database.d.ts +2 -2
  90. package/out/zero-server/src/zql-database.js +2 -2
  91. package/out/zero-server/src/zql-database.js.map +1 -1
  92. package/out/zero-solid/src/use-query.d.ts +2 -2
  93. package/out/zero-solid/src/use-query.js +1 -1
  94. package/out/zero-solid/src/use-query.js.map +1 -1
  95. package/out/zero-solid/src/use-zero-online.d.ts +1 -1
  96. package/out/zero-solid/src/use-zero-online.js +1 -1
  97. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  98. package/out/zero-solid/src/use-zero.d.ts +2 -2
  99. package/out/zero-solid/src/use-zero.js +2 -2
  100. package/out/zero-solid/src/use-zero.js.map +1 -1
  101. package/out/zql/src/ivm/change.d.ts +3 -3
  102. package/out/zql/src/ivm/change.js.map +1 -1
  103. package/out/zql/src/ivm/data.d.ts +1 -1
  104. package/out/zql/src/ivm/data.js.map +1 -1
  105. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
  106. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +1 -1
  107. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  108. package/out/zql/src/mutate/custom.d.ts +3 -3
  109. package/out/zql/src/mutate/custom.js.map +1 -1
  110. package/out/zql/src/query/create-builder.d.ts +1 -1
  111. package/out/zql/src/query/create-builder.d.ts.map +1 -1
  112. package/out/zql/src/query/create-builder.js +1 -1
  113. package/out/zql/src/query/create-builder.js.map +1 -1
  114. package/out/zql/src/query/named.d.ts +2 -2
  115. package/out/zql/src/query/named.js +2 -2
  116. package/out/zql/src/query/named.js.map +1 -1
  117. package/out/zql/src/query/query-impl.js +3 -3
  118. package/out/zql/src/query/query-impl.js.map +1 -1
  119. package/out/zql/src/query/query-internals.d.ts +5 -5
  120. package/out/zql/src/query/query-internals.js.map +1 -1
  121. package/out/zql/src/query/query-transitions.d.ts +5 -5
  122. package/out/zql/src/query/query-transitions.js +2 -2
  123. package/out/zql/src/query/query-transitions.js.map +1 -1
  124. package/out/zql/src/query/query.d.ts +8 -8
  125. package/package.json +1 -1
@@ -2,7 +2,7 @@ import * as v from '../../shared/src/valita.ts';
2
2
  import type { Read, Write } from './dag/store.ts';
3
3
  import { type ClientGroupID, type ClientID } from './sync/ids.ts';
4
4
  /**
5
- * We keep track of deleted clients in the {@linkcode DELETED_CLIENTS_HEAD_NAME}
5
+ * We keep track of deleted clients in the {@link DELETED_CLIENTS_HEAD_NAME}
6
6
  * head.
7
7
  */
8
8
  export declare const DELETED_CLIENTS_HEAD_NAME = "deleted-clients-v2";
@@ -19,7 +19,7 @@ export declare function removeFromDeletedClients(old: DeletedClients, toRemove:
19
19
  export declare function setDeletedClients(dagWrite: Write, deletedClients: DeletedClients): Promise<DeletedClients>;
20
20
  export declare function getDeletedClients(dagRead: Read): Promise<DeletedClients>;
21
21
  /**
22
- * Adds deleted clients to the {@linkcode DELETED_CLIENTS_HEAD_NAME} head.
22
+ * Adds deleted clients to the {@link DELETED_CLIENTS_HEAD_NAME} head.
23
23
  * @returns the new list of deleted clients (sorted and deduped).
24
24
  */
25
25
  export declare function addDeletedClients(dagWrite: Write, deletedClientsToAdd: DeletedClients): Promise<DeletedClients>;
@@ -5,7 +5,7 @@ import { clientGroupIDSchema, clientIDSchema } from "./sync/ids.js";
5
5
  import { getClients, setClients } from "./persist/clients.js";
6
6
  //#region ../replicache/src/deleted-clients.ts
7
7
  /**
8
- * We keep track of deleted clients in the {@linkcode DELETED_CLIENTS_HEAD_NAME}
8
+ * We keep track of deleted clients in the {@link DELETED_CLIENTS_HEAD_NAME}
9
9
  * head.
10
10
  */
11
11
  var DELETED_CLIENTS_HEAD_NAME = "deleted-clients-v2";
@@ -54,7 +54,7 @@ async function getDeletedClients(dagRead) {
54
54
  return res.value;
55
55
  }
56
56
  /**
57
- * Adds deleted clients to the {@linkcode DELETED_CLIENTS_HEAD_NAME} head.
57
+ * Adds deleted clients to the {@link DELETED_CLIENTS_HEAD_NAME} head.
58
58
  * @returns the new list of deleted clients (sorted and deduped).
59
59
  */
60
60
  async function addDeletedClients(dagWrite, deletedClientsToAdd) {
@@ -1 +1 @@
1
- {"version":3,"file":"deleted-clients.js","names":[],"sources":["../../../../replicache/src/deleted-clients.ts"],"sourcesContent":["import {stringCompare} from '../../shared/src/string-compare.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport type {Read, Write} from './dag/store.ts';\nimport {deepFreeze} from './frozen-json.ts';\nimport {getClients, setClients} from './persist/clients.ts';\nimport {\n clientGroupIDSchema,\n clientIDSchema,\n type ClientGroupID,\n type ClientID,\n} from './sync/ids.ts';\n\n/**\n * We keep track of deleted clients in the {@linkcode DELETED_CLIENTS_HEAD_NAME}\n * head.\n */\nexport const DELETED_CLIENTS_HEAD_NAME = 'deleted-clients-v2';\n\ntype ClientIDPair = {\n clientGroupID: ClientGroupID;\n clientID: ClientID;\n};\n\nexport type DeletedClients = readonly Readonly<ClientIDPair>[];\n\nexport type WritableDeletedClients = ClientIDPair[];\n\nexport const deletedClientsSchema: v.Type<DeletedClients> = v.readonlyArray(\n v.readonlyObject({\n clientGroupID: clientGroupIDSchema,\n clientID: clientIDSchema,\n }),\n);\n\nfunction compare(a: ClientIDPair, b: ClientIDPair): number {\n const cg = stringCompare(a.clientGroupID, b.clientGroupID);\n if (cg !== 0) {\n return cg;\n }\n return stringCompare(a.clientID, b.clientID);\n}\n\nexport function normalizeDeletedClients(\n deletedClients: DeletedClients,\n): DeletedClients {\n return deletedClients\n .toSorted(compare)\n .filter((item, i, arr) => i === 0 || compare(item, arr[i - 1]) !== 0);\n}\n\nexport function mergeDeletedClients(\n a: DeletedClients,\n b: DeletedClients,\n): DeletedClients {\n const merged: WritableDeletedClients = [];\n a = normalizeDeletedClients(a);\n b = normalizeDeletedClients(b);\n for (let i = 0, j = 0; i < a.length || j < b.length;) {\n if (i < a.length && (j >= b.length || compare(a[i], b[j]) < 0)) {\n merged.push(a[i]);\n i++;\n } else if (j < b.length && (i >= a.length || compare(b[j], a[i]) < 0)) {\n merged.push(b[j]);\n j++;\n } else {\n // equal\n merged.push(a[i]);\n i++;\n j++;\n }\n }\n return merged;\n}\n\nexport function removeFromDeletedClients(\n old: DeletedClients,\n toRemove: DeletedClients,\n): DeletedClients {\n old = normalizeDeletedClients(old);\n toRemove = normalizeDeletedClients(toRemove);\n const result: WritableDeletedClients = [];\n for (let i = 0, j = 0; i < old.length;) {\n if (j >= toRemove.length || compare(old[i], toRemove[j]) < 0) {\n result.push(old[i]);\n i++;\n } else if (j < toRemove.length && compare(old[i], toRemove[j]) === 0) {\n // equal, skip\n i++;\n j++;\n } else {\n // old[i] > toRemove[j]\n j++;\n }\n }\n return result;\n}\n\nexport async function setDeletedClients(\n dagWrite: Write,\n deletedClients: DeletedClients,\n): Promise<DeletedClients> {\n // sort and dedupe\n\n const data = normalizeDeletedClients(deletedClients);\n\n const chunkData = deepFreeze(data);\n const chunk = dagWrite.createChunk(chunkData, []);\n await dagWrite.putChunk(chunk);\n await dagWrite.setHead(DELETED_CLIENTS_HEAD_NAME, chunk.hash);\n return data;\n}\n\nexport async function getDeletedClients(\n dagRead: Read,\n): Promise<DeletedClients> {\n const hash = await dagRead.getHead(DELETED_CLIENTS_HEAD_NAME);\n if (hash === undefined) {\n return [];\n }\n const chunk = await dagRead.mustGetChunk(hash);\n\n const res = v.test(chunk.data, deletedClientsSchema);\n if (!res.ok) {\n // If not ok then we ignore this. It might be in the old format but we do\n // not know the clientGroupID of the old clients.\n return [];\n }\n\n return res.value;\n}\n\n/**\n * Adds deleted clients to the {@linkcode DELETED_CLIENTS_HEAD_NAME} head.\n * @returns the new list of deleted clients (sorted and deduped).\n */\nexport async function addDeletedClients(\n dagWrite: Write,\n deletedClientsToAdd: DeletedClients,\n): Promise<DeletedClients> {\n const oldDeletedClients = await getDeletedClients(dagWrite);\n\n return setDeletedClients(\n dagWrite,\n mergeDeletedClients(oldDeletedClients, deletedClientsToAdd),\n );\n}\n\nexport async function removeDeletedClients(\n dagWrite: Write,\n deletedClientsToRemove: DeletedClients,\n): Promise<DeletedClients> {\n const oldDeletedClients = await getDeletedClients(dagWrite);\n return setDeletedClients(\n dagWrite,\n removeFromDeletedClients(oldDeletedClients, deletedClientsToRemove),\n );\n}\n\nexport async function confirmDeletedClients(\n dagWrite: Write,\n deletedClientIds: readonly ClientID[],\n deletedClientGroupIds: readonly ClientGroupID[],\n): Promise<DeletedClients> {\n const deletedClientIDSet = new Set(deletedClientIds);\n const deletedClientGroupIDSet = new Set(deletedClientGroupIds);\n const oldDeletedClients = await getDeletedClients(dagWrite);\n const clients = new Map(await getClients(dagWrite));\n for (const clientID of deletedClientIds) {\n clients.delete(clientID);\n }\n for (const clientGroupID of deletedClientGroupIds) {\n for (const [clientID, client] of clients) {\n if (client.clientGroupID === clientGroupID) {\n clients.delete(clientID);\n }\n }\n }\n\n await setClients(clients, dagWrite);\n\n return setDeletedClients(\n dagWrite,\n oldDeletedClients.filter(\n ({clientGroupID, clientID}) =>\n !deletedClientGroupIDSet.has(clientGroupID) &&\n !deletedClientIDSet.has(clientID),\n ),\n );\n}\n"],"mappings":";;;;;;;;;;AAgBA,IAAa,4BAA4B;AAWzC,IAAa,uBAA+C,cAC1D,eAAiB;CACf,eAAe;CACf,UAAU;AACZ,CAAC,CACH;AAEA,SAAS,QAAQ,GAAiB,GAAyB;CACzD,MAAM,KAAK,cAAc,EAAE,eAAe,EAAE,aAAa;CACzD,IAAI,OAAO,GACT,OAAO;CAET,OAAO,cAAc,EAAE,UAAU,EAAE,QAAQ;AAC7C;AAEA,SAAgB,wBACd,gBACgB;CAChB,OAAO,eACJ,SAAS,OAAO,EAChB,QAAQ,MAAM,GAAG,QAAQ,MAAM,KAAK,QAAQ,MAAM,IAAI,IAAI,EAAE,MAAM,CAAC;AACxE;AAEA,SAAgB,oBACd,GACA,GACgB;CAChB,MAAM,SAAiC,CAAC;CACxC,IAAI,wBAAwB,CAAC;CAC7B,IAAI,wBAAwB,CAAC;CAC7B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,SAC3C,IAAI,IAAI,EAAE,WAAW,KAAK,EAAE,UAAU,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI;EAC9D,OAAO,KAAK,EAAE,EAAE;EAChB;CACF,OAAO,IAAI,IAAI,EAAE,WAAW,KAAK,EAAE,UAAU,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI;EACrE,OAAO,KAAK,EAAE,EAAE;EAChB;CACF,OAAO;EAEL,OAAO,KAAK,EAAE,EAAE;EAChB;EACA;CACF;CAEF,OAAO;AACT;AAyBA,eAAsB,kBACpB,UACA,gBACyB;CAGzB,MAAM,OAAO,wBAAwB,cAAc;CAEnD,MAAM,YAAY,WAAW,IAAI;CACjC,MAAM,QAAQ,SAAS,YAAY,WAAW,CAAC,CAAC;CAChD,MAAM,SAAS,SAAS,KAAK;CAC7B,MAAM,SAAS,QAAQ,2BAA2B,MAAM,IAAI;CAC5D,OAAO;AACT;AAEA,eAAsB,kBACpB,SACyB;CACzB,MAAM,OAAO,MAAM,QAAQ,QAAQ,yBAAyB;CAC5D,IAAI,SAAS,KAAA,GACX,OAAO,CAAC;CAIV,MAAM,MAAM,MAAO,MAFC,QAAQ,aAAa,IAAI,GAEpB,MAAM,oBAAoB;CACnD,IAAI,CAAC,IAAI,IAGP,OAAO,CAAC;CAGV,OAAO,IAAI;AACb;;;;;AAMA,eAAsB,kBACpB,UACA,qBACyB;CAGzB,OAAO,kBACL,UACA,oBAAoB,MAJU,kBAAkB,QAAQ,GAIjB,mBAAmB,CAC5D;AACF;AAaA,eAAsB,sBACpB,UACA,kBACA,uBACyB;CACzB,MAAM,qBAAqB,IAAI,IAAI,gBAAgB;CACnD,MAAM,0BAA0B,IAAI,IAAI,qBAAqB;CAC7D,MAAM,oBAAoB,MAAM,kBAAkB,QAAQ;CAC1D,MAAM,UAAU,IAAI,IAAI,MAAM,WAAW,QAAQ,CAAC;CAClD,KAAK,MAAM,YAAY,kBACrB,QAAQ,OAAO,QAAQ;CAEzB,KAAK,MAAM,iBAAiB,uBAC1B,KAAK,MAAM,CAAC,UAAU,WAAW,SAC/B,IAAI,OAAO,kBAAkB,eAC3B,QAAQ,OAAO,QAAQ;CAK7B,MAAM,WAAW,SAAS,QAAQ;CAElC,OAAO,kBACL,UACA,kBAAkB,QACf,EAAC,eAAe,eACf,CAAC,wBAAwB,IAAI,aAAa,KAC1C,CAAC,mBAAmB,IAAI,QAAQ,CACpC,CACF;AACF"}
1
+ {"version":3,"file":"deleted-clients.js","names":[],"sources":["../../../../replicache/src/deleted-clients.ts"],"sourcesContent":["import {stringCompare} from '../../shared/src/string-compare.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport type {Read, Write} from './dag/store.ts';\nimport {deepFreeze} from './frozen-json.ts';\nimport {getClients, setClients} from './persist/clients.ts';\nimport {\n clientGroupIDSchema,\n clientIDSchema,\n type ClientGroupID,\n type ClientID,\n} from './sync/ids.ts';\n\n/**\n * We keep track of deleted clients in the {@link DELETED_CLIENTS_HEAD_NAME}\n * head.\n */\nexport const DELETED_CLIENTS_HEAD_NAME = 'deleted-clients-v2';\n\ntype ClientIDPair = {\n clientGroupID: ClientGroupID;\n clientID: ClientID;\n};\n\nexport type DeletedClients = readonly Readonly<ClientIDPair>[];\n\nexport type WritableDeletedClients = ClientIDPair[];\n\nexport const deletedClientsSchema: v.Type<DeletedClients> = v.readonlyArray(\n v.readonlyObject({\n clientGroupID: clientGroupIDSchema,\n clientID: clientIDSchema,\n }),\n);\n\nfunction compare(a: ClientIDPair, b: ClientIDPair): number {\n const cg = stringCompare(a.clientGroupID, b.clientGroupID);\n if (cg !== 0) {\n return cg;\n }\n return stringCompare(a.clientID, b.clientID);\n}\n\nexport function normalizeDeletedClients(\n deletedClients: DeletedClients,\n): DeletedClients {\n return deletedClients\n .toSorted(compare)\n .filter((item, i, arr) => i === 0 || compare(item, arr[i - 1]) !== 0);\n}\n\nexport function mergeDeletedClients(\n a: DeletedClients,\n b: DeletedClients,\n): DeletedClients {\n const merged: WritableDeletedClients = [];\n a = normalizeDeletedClients(a);\n b = normalizeDeletedClients(b);\n for (let i = 0, j = 0; i < a.length || j < b.length;) {\n if (i < a.length && (j >= b.length || compare(a[i], b[j]) < 0)) {\n merged.push(a[i]);\n i++;\n } else if (j < b.length && (i >= a.length || compare(b[j], a[i]) < 0)) {\n merged.push(b[j]);\n j++;\n } else {\n // equal\n merged.push(a[i]);\n i++;\n j++;\n }\n }\n return merged;\n}\n\nexport function removeFromDeletedClients(\n old: DeletedClients,\n toRemove: DeletedClients,\n): DeletedClients {\n old = normalizeDeletedClients(old);\n toRemove = normalizeDeletedClients(toRemove);\n const result: WritableDeletedClients = [];\n for (let i = 0, j = 0; i < old.length;) {\n if (j >= toRemove.length || compare(old[i], toRemove[j]) < 0) {\n result.push(old[i]);\n i++;\n } else if (j < toRemove.length && compare(old[i], toRemove[j]) === 0) {\n // equal, skip\n i++;\n j++;\n } else {\n // old[i] > toRemove[j]\n j++;\n }\n }\n return result;\n}\n\nexport async function setDeletedClients(\n dagWrite: Write,\n deletedClients: DeletedClients,\n): Promise<DeletedClients> {\n // sort and dedupe\n\n const data = normalizeDeletedClients(deletedClients);\n\n const chunkData = deepFreeze(data);\n const chunk = dagWrite.createChunk(chunkData, []);\n await dagWrite.putChunk(chunk);\n await dagWrite.setHead(DELETED_CLIENTS_HEAD_NAME, chunk.hash);\n return data;\n}\n\nexport async function getDeletedClients(\n dagRead: Read,\n): Promise<DeletedClients> {\n const hash = await dagRead.getHead(DELETED_CLIENTS_HEAD_NAME);\n if (hash === undefined) {\n return [];\n }\n const chunk = await dagRead.mustGetChunk(hash);\n\n const res = v.test(chunk.data, deletedClientsSchema);\n if (!res.ok) {\n // If not ok then we ignore this. It might be in the old format but we do\n // not know the clientGroupID of the old clients.\n return [];\n }\n\n return res.value;\n}\n\n/**\n * Adds deleted clients to the {@link DELETED_CLIENTS_HEAD_NAME} head.\n * @returns the new list of deleted clients (sorted and deduped).\n */\nexport async function addDeletedClients(\n dagWrite: Write,\n deletedClientsToAdd: DeletedClients,\n): Promise<DeletedClients> {\n const oldDeletedClients = await getDeletedClients(dagWrite);\n\n return setDeletedClients(\n dagWrite,\n mergeDeletedClients(oldDeletedClients, deletedClientsToAdd),\n );\n}\n\nexport async function removeDeletedClients(\n dagWrite: Write,\n deletedClientsToRemove: DeletedClients,\n): Promise<DeletedClients> {\n const oldDeletedClients = await getDeletedClients(dagWrite);\n return setDeletedClients(\n dagWrite,\n removeFromDeletedClients(oldDeletedClients, deletedClientsToRemove),\n );\n}\n\nexport async function confirmDeletedClients(\n dagWrite: Write,\n deletedClientIds: readonly ClientID[],\n deletedClientGroupIds: readonly ClientGroupID[],\n): Promise<DeletedClients> {\n const deletedClientIDSet = new Set(deletedClientIds);\n const deletedClientGroupIDSet = new Set(deletedClientGroupIds);\n const oldDeletedClients = await getDeletedClients(dagWrite);\n const clients = new Map(await getClients(dagWrite));\n for (const clientID of deletedClientIds) {\n clients.delete(clientID);\n }\n for (const clientGroupID of deletedClientGroupIds) {\n for (const [clientID, client] of clients) {\n if (client.clientGroupID === clientGroupID) {\n clients.delete(clientID);\n }\n }\n }\n\n await setClients(clients, dagWrite);\n\n return setDeletedClients(\n dagWrite,\n oldDeletedClients.filter(\n ({clientGroupID, clientID}) =>\n !deletedClientGroupIDSet.has(clientGroupID) &&\n !deletedClientIDSet.has(clientID),\n ),\n );\n}\n"],"mappings":";;;;;;;;;;AAgBA,IAAa,4BAA4B;AAWzC,IAAa,uBAA+C,cAC1D,eAAiB;CACf,eAAe;CACf,UAAU;AACZ,CAAC,CACH;AAEA,SAAS,QAAQ,GAAiB,GAAyB;CACzD,MAAM,KAAK,cAAc,EAAE,eAAe,EAAE,aAAa;CACzD,IAAI,OAAO,GACT,OAAO;CAET,OAAO,cAAc,EAAE,UAAU,EAAE,QAAQ;AAC7C;AAEA,SAAgB,wBACd,gBACgB;CAChB,OAAO,eACJ,SAAS,OAAO,EAChB,QAAQ,MAAM,GAAG,QAAQ,MAAM,KAAK,QAAQ,MAAM,IAAI,IAAI,EAAE,MAAM,CAAC;AACxE;AAEA,SAAgB,oBACd,GACA,GACgB;CAChB,MAAM,SAAiC,CAAC;CACxC,IAAI,wBAAwB,CAAC;CAC7B,IAAI,wBAAwB,CAAC;CAC7B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,SAC3C,IAAI,IAAI,EAAE,WAAW,KAAK,EAAE,UAAU,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI;EAC9D,OAAO,KAAK,EAAE,EAAE;EAChB;CACF,OAAO,IAAI,IAAI,EAAE,WAAW,KAAK,EAAE,UAAU,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI;EACrE,OAAO,KAAK,EAAE,EAAE;EAChB;CACF,OAAO;EAEL,OAAO,KAAK,EAAE,EAAE;EAChB;EACA;CACF;CAEF,OAAO;AACT;AAyBA,eAAsB,kBACpB,UACA,gBACyB;CAGzB,MAAM,OAAO,wBAAwB,cAAc;CAEnD,MAAM,YAAY,WAAW,IAAI;CACjC,MAAM,QAAQ,SAAS,YAAY,WAAW,CAAC,CAAC;CAChD,MAAM,SAAS,SAAS,KAAK;CAC7B,MAAM,SAAS,QAAQ,2BAA2B,MAAM,IAAI;CAC5D,OAAO;AACT;AAEA,eAAsB,kBACpB,SACyB;CACzB,MAAM,OAAO,MAAM,QAAQ,QAAQ,yBAAyB;CAC5D,IAAI,SAAS,KAAA,GACX,OAAO,CAAC;CAIV,MAAM,MAAM,MAAO,MAFC,QAAQ,aAAa,IAAI,GAEpB,MAAM,oBAAoB;CACnD,IAAI,CAAC,IAAI,IAGP,OAAO,CAAC;CAGV,OAAO,IAAI;AACb;;;;;AAMA,eAAsB,kBACpB,UACA,qBACyB;CAGzB,OAAO,kBACL,UACA,oBAAoB,MAJU,kBAAkB,QAAQ,GAIjB,mBAAmB,CAC5D;AACF;AAaA,eAAsB,sBACpB,UACA,kBACA,uBACyB;CACzB,MAAM,qBAAqB,IAAI,IAAI,gBAAgB;CACnD,MAAM,0BAA0B,IAAI,IAAI,qBAAqB;CAC7D,MAAM,oBAAoB,MAAM,kBAAkB,QAAQ;CAC1D,MAAM,UAAU,IAAI,IAAI,MAAM,WAAW,QAAQ,CAAC;CAClD,KAAK,MAAM,YAAY,kBACrB,QAAQ,OAAO,QAAQ;CAEzB,KAAK,MAAM,iBAAiB,uBAC1B,KAAK,MAAM,CAAC,UAAU,WAAW,SAC/B,IAAI,OAAO,kBAAkB,eAC3B,QAAQ,OAAO,QAAQ;CAK7B,MAAM,WAAW,SAAS,QAAQ;CAElC,OAAO,kBACL,UACA,kBAAkB,QACf,EAAC,eAAe,eACf,CAAC,wBAAwB,IAAI,aAAa,KAC1C,CAAC,mBAAmB,IAAI,QAAQ,CACpC,CACF;AACF"}
@@ -6,7 +6,7 @@ export declare function getLatestGCUpdate(): Promise<ClientGroupMap> | undefined
6
6
  export declare function initClientGroupGC(dagStore: Store, enableMutationRecovery: boolean, lc: LogContext, signal: AbortSignal, onStorageFailure?: ((failure: StorageFailureError) => void) | undefined): void;
7
7
  /**
8
8
  * This removes client groups that have no clients and no pending mutations.
9
- * If {@linkcode enableMutationRecovery} is true, it will keep client groups with
9
+ * If {@link enableMutationRecovery} is true, it will keep client groups with
10
10
  * pending mutations. If it is false, it will remove client groups even when they
11
11
  * have pending mutations.
12
12
  */
@@ -13,7 +13,7 @@ function initClientGroupGC(dagStore, enableMutationRecovery, lc, signal, onStora
13
13
  }
14
14
  /**
15
15
  * This removes client groups that have no clients and no pending mutations.
16
- * If {@linkcode enableMutationRecovery} is true, it will keep client groups with
16
+ * If {@link enableMutationRecovery} is true, it will keep client groups with
17
17
  * pending mutations. If it is false, it will remove client groups even when they
18
18
  * have pending mutations.
19
19
  */
@@ -1 +1 @@
1
- {"version":3,"file":"client-group-gc.js","names":[],"sources":["../../../../../replicache/src/persist/client-group-gc.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport type {Store} from '../dag/store.ts';\nimport type {StorageFailureError} from '../storage-failure.ts';\nimport type {ClientGroupID} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n setClientGroups,\n type ClientGroupMap,\n} from './client-groups.ts';\nimport {getClients} from './clients.ts';\n\nconst GC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n\nlet latestGCUpdate: Promise<ClientGroupMap> | undefined;\nexport function getLatestGCUpdate(): Promise<ClientGroupMap> | undefined {\n return latestGCUpdate;\n}\n\nexport function initClientGroupGC(\n dagStore: Store,\n enableMutationRecovery: boolean,\n lc: LogContext,\n signal: AbortSignal,\n onStorageFailure?: ((failure: StorageFailureError) => void) | undefined,\n): void {\n initBgIntervalProcess(\n 'ClientGroupGC',\n () => {\n latestGCUpdate = gcClientGroups(dagStore, enableMutationRecovery);\n return latestGCUpdate;\n },\n () => GC_INTERVAL_MS,\n lc,\n signal,\n onStorageFailure,\n );\n}\n\n/**\n * This removes client groups that have no clients and no pending mutations.\n * If {@linkcode enableMutationRecovery} is true, it will keep client groups with\n * pending mutations. If it is false, it will remove client groups even when they\n * have pending mutations.\n */\nexport function gcClientGroups(\n dagStore: Store,\n enableMutationRecovery: boolean,\n): Promise<ClientGroupMap> {\n return withWrite(dagStore, async tx => {\n const clients = await getClients(tx);\n const clientGroupIDs = new Set();\n for (const client of clients.values()) {\n clientGroupIDs.add(client.clientGroupID);\n }\n const clientGroups = new Map();\n const removeClientGroups: Set<ClientGroupID> = new Set();\n for (const [clientGroupID, clientGroup] of await getClientGroups(tx)) {\n if (\n clientGroupIDs.has(clientGroupID) ||\n (enableMutationRecovery && clientGroupHasPendingMutations(clientGroup))\n ) {\n clientGroups.set(clientGroupID, clientGroup);\n } else {\n removeClientGroups.add(clientGroupID);\n }\n }\n await setClientGroups(clientGroups, tx);\n // Client group GC doesn't delete individual clients, so we don't call onClientsDeleted\n return clientGroups;\n });\n}\n"],"mappings":";;;;;AAcA,IAAM,iBAAiB,MAAS;AAEhC,IAAI;AAKJ,SAAgB,kBACd,UACA,wBACA,IACA,QACA,kBACM;CACN,sBACE,uBACM;EACJ,iBAAiB,eAAe,UAAU,sBAAsB;EAChE,OAAO;CACT,SACM,gBACN,IACA,QACA,gBACF;AACF;;;;;;;AAQA,SAAgB,eACd,UACA,wBACyB;CACzB,OAAO,UAAU,UAAU,OAAM,OAAM;EACrC,MAAM,UAAU,MAAM,WAAW,EAAE;EACnC,MAAM,iCAAiB,IAAI,IAAI;EAC/B,KAAK,MAAM,UAAU,QAAQ,OAAO,GAClC,eAAe,IAAI,OAAO,aAAa;EAEzC,MAAM,+BAAe,IAAI,IAAI;EAC7B,MAAM,qCAAyC,IAAI,IAAI;EACvD,KAAK,MAAM,CAAC,eAAe,gBAAgB,MAAM,gBAAgB,EAAE,GACjE,IACE,eAAe,IAAI,aAAa,KAC/B,0BAA0B,+BAA+B,WAAW,GAErE,aAAa,IAAI,eAAe,WAAW;OAE3C,mBAAmB,IAAI,aAAa;EAGxC,MAAM,gBAAgB,cAAc,EAAE;EAEtC,OAAO;CACT,CAAC;AACH"}
1
+ {"version":3,"file":"client-group-gc.js","names":[],"sources":["../../../../../replicache/src/persist/client-group-gc.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport type {Store} from '../dag/store.ts';\nimport type {StorageFailureError} from '../storage-failure.ts';\nimport type {ClientGroupID} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n setClientGroups,\n type ClientGroupMap,\n} from './client-groups.ts';\nimport {getClients} from './clients.ts';\n\nconst GC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n\nlet latestGCUpdate: Promise<ClientGroupMap> | undefined;\nexport function getLatestGCUpdate(): Promise<ClientGroupMap> | undefined {\n return latestGCUpdate;\n}\n\nexport function initClientGroupGC(\n dagStore: Store,\n enableMutationRecovery: boolean,\n lc: LogContext,\n signal: AbortSignal,\n onStorageFailure?: ((failure: StorageFailureError) => void) | undefined,\n): void {\n initBgIntervalProcess(\n 'ClientGroupGC',\n () => {\n latestGCUpdate = gcClientGroups(dagStore, enableMutationRecovery);\n return latestGCUpdate;\n },\n () => GC_INTERVAL_MS,\n lc,\n signal,\n onStorageFailure,\n );\n}\n\n/**\n * This removes client groups that have no clients and no pending mutations.\n * If {@link enableMutationRecovery} is true, it will keep client groups with\n * pending mutations. If it is false, it will remove client groups even when they\n * have pending mutations.\n */\nexport function gcClientGroups(\n dagStore: Store,\n enableMutationRecovery: boolean,\n): Promise<ClientGroupMap> {\n return withWrite(dagStore, async tx => {\n const clients = await getClients(tx);\n const clientGroupIDs = new Set();\n for (const client of clients.values()) {\n clientGroupIDs.add(client.clientGroupID);\n }\n const clientGroups = new Map();\n const removeClientGroups: Set<ClientGroupID> = new Set();\n for (const [clientGroupID, clientGroup] of await getClientGroups(tx)) {\n if (\n clientGroupIDs.has(clientGroupID) ||\n (enableMutationRecovery && clientGroupHasPendingMutations(clientGroup))\n ) {\n clientGroups.set(clientGroupID, clientGroup);\n } else {\n removeClientGroups.add(clientGroupID);\n }\n }\n await setClientGroups(clientGroups, tx);\n // Client group GC doesn't delete individual clients, so we don't call onClientsDeleted\n return clientGroups;\n });\n}\n"],"mappings":";;;;;AAcA,IAAM,iBAAiB,MAAS;AAEhC,IAAI;AAKJ,SAAgB,kBACd,UACA,wBACA,IACA,QACA,kBACM;CACN,sBACE,uBACM;EACJ,iBAAiB,eAAe,UAAU,sBAAsB;EAChE,OAAO;CACT,SACM,gBACN,IACA,QACA,gBACF;AACF;;;;;;;AAQA,SAAgB,eACd,UACA,wBACyB;CACzB,OAAO,UAAU,UAAU,OAAM,OAAM;EACrC,MAAM,UAAU,MAAM,WAAW,EAAE;EACnC,MAAM,iCAAiB,IAAI,IAAI;EAC/B,KAAK,MAAM,UAAU,QAAQ,OAAO,GAClC,eAAe,IAAI,OAAO,aAAa;EAEzC,MAAM,+BAAe,IAAI,IAAI;EAC7B,MAAM,qCAAyC,IAAI,IAAI;EACvD,KAAK,MAAM,CAAC,eAAe,gBAAgB,MAAM,gBAAgB,EAAE,GACjE,IACE,eAAe,IAAI,aAAa,KAC/B,0BAA0B,+BAA+B,WAAW,GAErE,aAAa,IAAI,eAAe,WAAW;OAE3C,mBAAmB,IAAI,aAAa;EAGxC,MAAM,gBAAgB,cAAc,EAAE;EAEtC,OAAO;CACT,CAAC;AACH"}
@@ -204,7 +204,7 @@ export interface ReplicacheOptions<MD extends MutatorDefs> {
204
204
  * This means that this is the maximum time a tab can be in the background
205
205
  * (frozen or in fbcache) and still be able to sync when it comes back to the
206
206
  * foreground. If tab comes back after this time the
207
- * {@linkcode onClientStateNotFound} callback is called on the Replicache
207
+ * {@link onClientStateNotFound} callback is called on the Replicache
208
208
  * instance.
209
209
  */
210
210
  clientMaxAgeMs?: number | undefined;
@@ -7,7 +7,7 @@ export declare function getBrowserGlobal<T extends keyof GlobalThis>(name: T): G
7
7
  * This is important because some methods (e.g. `requestAnimationFrame`) are not
8
8
  * bound to the global object by default.
9
9
  *
10
- * If you end up using {@linkcode getBrowserGlobal} instead in a case like this:
10
+ * If you end up using {@link getBrowserGlobal} instead in a case like this:
11
11
  *
12
12
  * ```js
13
13
  * this.#raf = getBrowserGlobal('requestAnimationFrame') ?? rafFallback;
@@ -1 +1 @@
1
- {"version":3,"file":"browser-env.js","names":[],"sources":["../../../../shared/src/browser-env.ts"],"sourcesContent":["// Helpers for some objects from the browser environment. These are wrapped in\n// functions because Replicache runs in environments that do not have these\n// objects (such as Web Workers, Deno etc).\n\ntype GlobalThis = typeof globalThis;\n\nconst overrides = new Map<keyof GlobalThis, GlobalThis[keyof GlobalThis]>();\n\nexport function overrideBrowserGlobal<T extends keyof GlobalThis>(\n name: T,\n value: GlobalThis[T],\n) {\n overrides.set(name, value);\n}\n\nexport function clearBrowserOverrides() {\n overrides.clear();\n}\n\nexport function getBrowserGlobal<T extends keyof GlobalThis>(\n name: T,\n): GlobalThis[T] | undefined {\n if (overrides.has(name)) {\n return overrides.get(name);\n }\n return globalThis[name];\n}\n\n/**\n * Returns the global method with the given name, bound to the global object.\n * This is important because some methods (e.g. `requestAnimationFrame`) are not\n * bound to the global object by default.\n *\n * If you end up using {@linkcode getBrowserGlobal} instead in a case like this:\n *\n * ```js\n * this.#raf = getBrowserGlobal('requestAnimationFrame') ?? rafFallback;\n * ...\n * this.#raf(() => ...);\n * ```\n *\n * You will end up with `Uncaught TypeError: Illegal invocation` because `this`\n * is not bound to the global object\n */\nexport function getBrowserGlobalMethod<T extends keyof GlobalThis>(\n name: T,\n): GlobalThis[T] | undefined {\n return getBrowserGlobal(name)?.bind(globalThis);\n}\n\nexport function mustGetBrowserGlobal<T extends keyof GlobalThis>(\n name: T,\n): GlobalThis[T] {\n const r = getBrowserGlobal(name);\n if (r === undefined) {\n throw new Error(\n `Unsupported JavaScript environment: Could not find ${name}.`,\n );\n }\n return r;\n}\n"],"mappings":";AAMA,IAAM,4BAAY,IAAI,IAAoD;AAa1E,SAAgB,iBACd,MAC2B;CAC3B,IAAI,UAAU,IAAI,IAAI,GACpB,OAAO,UAAU,IAAI,IAAI;CAE3B,OAAO,WAAW;AACpB;AAwBA,SAAgB,qBACd,MACe;CACf,MAAM,IAAI,iBAAiB,IAAI;CAC/B,IAAI,MAAM,KAAA,GACR,MAAM,IAAI,MACR,sDAAsD,KAAK,EAC7D;CAEF,OAAO;AACT"}
1
+ {"version":3,"file":"browser-env.js","names":[],"sources":["../../../../shared/src/browser-env.ts"],"sourcesContent":["// Helpers for some objects from the browser environment. These are wrapped in\n// functions because Replicache runs in environments that do not have these\n// objects (such as Web Workers, Deno etc).\n\ntype GlobalThis = typeof globalThis;\n\nconst overrides = new Map<keyof GlobalThis, GlobalThis[keyof GlobalThis]>();\n\nexport function overrideBrowserGlobal<T extends keyof GlobalThis>(\n name: T,\n value: GlobalThis[T],\n) {\n overrides.set(name, value);\n}\n\nexport function clearBrowserOverrides() {\n overrides.clear();\n}\n\nexport function getBrowserGlobal<T extends keyof GlobalThis>(\n name: T,\n): GlobalThis[T] | undefined {\n if (overrides.has(name)) {\n return overrides.get(name);\n }\n return globalThis[name];\n}\n\n/**\n * Returns the global method with the given name, bound to the global object.\n * This is important because some methods (e.g. `requestAnimationFrame`) are not\n * bound to the global object by default.\n *\n * If you end up using {@link getBrowserGlobal} instead in a case like this:\n *\n * ```js\n * this.#raf = getBrowserGlobal('requestAnimationFrame') ?? rafFallback;\n * ...\n * this.#raf(() => ...);\n * ```\n *\n * You will end up with `Uncaught TypeError: Illegal invocation` because `this`\n * is not bound to the global object\n */\nexport function getBrowserGlobalMethod<T extends keyof GlobalThis>(\n name: T,\n): GlobalThis[T] | undefined {\n return getBrowserGlobal(name)?.bind(globalThis);\n}\n\nexport function mustGetBrowserGlobal<T extends keyof GlobalThis>(\n name: T,\n): GlobalThis[T] {\n const r = getBrowserGlobal(name);\n if (r === undefined) {\n throw new Error(\n `Unsupported JavaScript environment: Could not find ${name}.`,\n );\n }\n return r;\n}\n"],"mappings":";AAMA,IAAM,4BAAY,IAAI,IAAoD;AAa1E,SAAgB,iBACd,MAC2B;CAC3B,IAAI,UAAU,IAAI,IAAI,GACpB,OAAO,UAAU,IAAI,IAAI;CAE3B,OAAO,WAAW;AACpB;AAwBA,SAAgB,qBACd,MACe;CACf,MAAM,IAAI,iBAAiB,IAAI;CAC/B,IAAI,MAAM,KAAA,GACR,MAAM,IAAI,MACR,sDAAsD,KAAK,EAC7D;CAEF,OAAO;AACT"}
@@ -16,7 +16,7 @@ export declare function equals<T>(a: ReadonlySetLike<T>, b: ReadonlySetLike<T>):
16
16
  export declare function union<T>(...sets: ReadonlySetLike<T>[]): Set<T>;
17
17
  export declare function intersection<T>(a: ReadonlySetLike<T>, b: ReadonlySetLike<T>): Set<T>;
18
18
  /**
19
- * Returns the elements in {@linkcode a} that are not in {@linkcode b}.
19
+ * Returns the elements in {@link a} that are not in {@link b}.
20
20
  */
21
21
  export declare function difference<T>(a: ReadonlySetLike<T>, b: ReadonlySetLike<T>): Set<T>;
22
22
  export declare function symmetricDifferences<T>(a: ReadonlySetLike<T>, b: ReadonlySetLike<T>): [onlyA: Set<T>, onlyB: Set<T>];
@@ -25,7 +25,7 @@ function intersection(a, b) {
25
25
  return result;
26
26
  }
27
27
  /**
28
- * Returns the elements in {@linkcode a} that are not in {@linkcode b}.
28
+ * Returns the elements in {@link a} that are not in {@link b}.
29
29
  */
30
30
  function difference(a, b) {
31
31
  const result = /* @__PURE__ */ new Set();
@@ -1 +1 @@
1
- {"version":3,"file":"set-utils.js","names":[],"sources":["../../../../shared/src/set-utils.ts"],"sourcesContent":["// TODO: Change lib to pick up this type definition\n\ninterface ReadonlySetLike<T> {\n /**\n * Despite its name, returns an iterator of the values in the set-like.\n */\n keys(): Iterator<T>;\n /**\n * @returns a boolean indicating whether an element with the specified value exists in the set-like or not.\n */\n has(value: T): boolean;\n /**\n * @returns the number of (unique) elements in the set-like.\n */\n readonly size: number;\n}\n\nexport function equals<T>(\n a: ReadonlySetLike<T>,\n b: ReadonlySetLike<T>,\n): boolean {\n if (a.size !== b.size) {\n return false;\n }\n const iterator = a.keys();\n for (\n let {done, value} = iterator.next();\n !done;\n {done, value} = iterator.next()\n ) {\n if (!b.has(value)) {\n return false;\n }\n }\n return true;\n}\n\nexport function union<T>(...sets: ReadonlySetLike<T>[]): Set<T> {\n const result = new Set<T>();\n for (const set of sets) {\n const iterator = set.keys();\n for (\n let {done, value} = iterator.next();\n !done;\n {done, value} = iterator.next()\n ) {\n result.add(value);\n }\n }\n return result;\n}\n\nexport function intersection<T>(\n a: ReadonlySetLike<T>,\n b: ReadonlySetLike<T>,\n): Set<T> {\n const result = new Set<T>();\n if (a.size > b.size) {\n // Optimization: iterate over the smaller Set.\n const swap = a;\n a = b;\n b = swap;\n }\n const iterator = a.keys();\n for (\n let {done, value} = iterator.next();\n !done;\n {done, value} = iterator.next()\n ) {\n if (b.has(value)) {\n result.add(value);\n }\n }\n return result;\n}\n\n/**\n * Returns the elements in {@linkcode a} that are not in {@linkcode b}.\n */\nexport function difference<T>(\n a: ReadonlySetLike<T>,\n b: ReadonlySetLike<T>,\n): Set<T> {\n const result = new Set<T>();\n const iterator = a.keys();\n for (\n let {done, value} = iterator.next();\n !done;\n {done, value} = iterator.next()\n ) {\n if (!b.has(value)) {\n result.add(value);\n }\n }\n return result;\n}\n\nexport function symmetricDifferences<T>(\n a: ReadonlySetLike<T>,\n b: ReadonlySetLike<T>,\n): [onlyA: Set<T>, onlyB: Set<T>] {\n const onlyA = new Set<T>();\n const iteratorA = a.keys();\n for (\n let {done, value} = iteratorA.next();\n !done;\n {done, value} = iteratorA.next()\n ) {\n onlyA.add(value);\n }\n\n const onlyB = new Set<T>();\n const iteratorB = b.keys();\n for (\n let {done, value} = iteratorB.next();\n !done;\n {done, value} = iteratorB.next()\n ) {\n if (a.has(value)) {\n onlyA.delete(value);\n onlyB.delete(value);\n } else {\n onlyB.add(value);\n }\n }\n return [onlyA, onlyB];\n}\n"],"mappings":";AAiBA,SAAgB,OACd,GACA,GACS;CACT,IAAI,EAAE,SAAS,EAAE,MACf,OAAO;CAET,MAAM,WAAW,EAAE,KAAK;CACxB,KACE,IAAI,EAAC,MAAM,UAAS,SAAS,KAAK,GAClC,CAAC,MACD,CAAC,MAAM,SAAS,SAAS,KAAK,GAE9B,IAAI,CAAC,EAAE,IAAI,KAAK,GACd,OAAO;CAGX,OAAO;AACT;AAEA,SAAgB,MAAS,GAAG,MAAoC;CAC9D,MAAM,yBAAS,IAAI,IAAO;CAC1B,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAAW,IAAI,KAAK;EAC1B,KACE,IAAI,EAAC,MAAM,UAAS,SAAS,KAAK,GAClC,CAAC,MACD,CAAC,MAAM,SAAS,SAAS,KAAK,GAE9B,OAAO,IAAI,KAAK;CAEpB;CACA,OAAO;AACT;AAEA,SAAgB,aACd,GACA,GACQ;CACR,MAAM,yBAAS,IAAI,IAAO;CAC1B,IAAI,EAAE,OAAO,EAAE,MAAM;EAEnB,MAAM,OAAO;EACb,IAAI;EACJ,IAAI;CACN;CACA,MAAM,WAAW,EAAE,KAAK;CACxB,KACE,IAAI,EAAC,MAAM,UAAS,SAAS,KAAK,GAClC,CAAC,MACD,CAAC,MAAM,SAAS,SAAS,KAAK,GAE9B,IAAI,EAAE,IAAI,KAAK,GACb,OAAO,IAAI,KAAK;CAGpB,OAAO;AACT;;;;AAKA,SAAgB,WACd,GACA,GACQ;CACR,MAAM,yBAAS,IAAI,IAAO;CAC1B,MAAM,WAAW,EAAE,KAAK;CACxB,KACE,IAAI,EAAC,MAAM,UAAS,SAAS,KAAK,GAClC,CAAC,MACD,CAAC,MAAM,SAAS,SAAS,KAAK,GAE9B,IAAI,CAAC,EAAE,IAAI,KAAK,GACd,OAAO,IAAI,KAAK;CAGpB,OAAO;AACT;AAEA,SAAgB,qBACd,GACA,GACgC;CAChC,MAAM,wBAAQ,IAAI,IAAO;CACzB,MAAM,YAAY,EAAE,KAAK;CACzB,KACE,IAAI,EAAC,MAAM,UAAS,UAAU,KAAK,GACnC,CAAC,MACD,CAAC,MAAM,SAAS,UAAU,KAAK,GAE/B,MAAM,IAAI,KAAK;CAGjB,MAAM,wBAAQ,IAAI,IAAO;CACzB,MAAM,YAAY,EAAE,KAAK;CACzB,KACE,IAAI,EAAC,MAAM,UAAS,UAAU,KAAK,GACnC,CAAC,MACD,CAAC,MAAM,SAAS,UAAU,KAAK,GAE/B,IAAI,EAAE,IAAI,KAAK,GAAG;EAChB,MAAM,OAAO,KAAK;EAClB,MAAM,OAAO,KAAK;CACpB,OACE,MAAM,IAAI,KAAK;CAGnB,OAAO,CAAC,OAAO,KAAK;AACtB"}
1
+ {"version":3,"file":"set-utils.js","names":[],"sources":["../../../../shared/src/set-utils.ts"],"sourcesContent":["// TODO: Change lib to pick up this type definition\n\ninterface ReadonlySetLike<T> {\n /**\n * Despite its name, returns an iterator of the values in the set-like.\n */\n keys(): Iterator<T>;\n /**\n * @returns a boolean indicating whether an element with the specified value exists in the set-like or not.\n */\n has(value: T): boolean;\n /**\n * @returns the number of (unique) elements in the set-like.\n */\n readonly size: number;\n}\n\nexport function equals<T>(\n a: ReadonlySetLike<T>,\n b: ReadonlySetLike<T>,\n): boolean {\n if (a.size !== b.size) {\n return false;\n }\n const iterator = a.keys();\n for (\n let {done, value} = iterator.next();\n !done;\n {done, value} = iterator.next()\n ) {\n if (!b.has(value)) {\n return false;\n }\n }\n return true;\n}\n\nexport function union<T>(...sets: ReadonlySetLike<T>[]): Set<T> {\n const result = new Set<T>();\n for (const set of sets) {\n const iterator = set.keys();\n for (\n let {done, value} = iterator.next();\n !done;\n {done, value} = iterator.next()\n ) {\n result.add(value);\n }\n }\n return result;\n}\n\nexport function intersection<T>(\n a: ReadonlySetLike<T>,\n b: ReadonlySetLike<T>,\n): Set<T> {\n const result = new Set<T>();\n if (a.size > b.size) {\n // Optimization: iterate over the smaller Set.\n const swap = a;\n a = b;\n b = swap;\n }\n const iterator = a.keys();\n for (\n let {done, value} = iterator.next();\n !done;\n {done, value} = iterator.next()\n ) {\n if (b.has(value)) {\n result.add(value);\n }\n }\n return result;\n}\n\n/**\n * Returns the elements in {@link a} that are not in {@link b}.\n */\nexport function difference<T>(\n a: ReadonlySetLike<T>,\n b: ReadonlySetLike<T>,\n): Set<T> {\n const result = new Set<T>();\n const iterator = a.keys();\n for (\n let {done, value} = iterator.next();\n !done;\n {done, value} = iterator.next()\n ) {\n if (!b.has(value)) {\n result.add(value);\n }\n }\n return result;\n}\n\nexport function symmetricDifferences<T>(\n a: ReadonlySetLike<T>,\n b: ReadonlySetLike<T>,\n): [onlyA: Set<T>, onlyB: Set<T>] {\n const onlyA = new Set<T>();\n const iteratorA = a.keys();\n for (\n let {done, value} = iteratorA.next();\n !done;\n {done, value} = iteratorA.next()\n ) {\n onlyA.add(value);\n }\n\n const onlyB = new Set<T>();\n const iteratorB = b.keys();\n for (\n let {done, value} = iteratorB.next();\n !done;\n {done, value} = iteratorB.next()\n ) {\n if (a.has(value)) {\n onlyA.delete(value);\n onlyB.delete(value);\n } else {\n onlyB.add(value);\n }\n }\n return [onlyA, onlyB];\n}\n"],"mappings":";AAiBA,SAAgB,OACd,GACA,GACS;CACT,IAAI,EAAE,SAAS,EAAE,MACf,OAAO;CAET,MAAM,WAAW,EAAE,KAAK;CACxB,KACE,IAAI,EAAC,MAAM,UAAS,SAAS,KAAK,GAClC,CAAC,MACD,CAAC,MAAM,SAAS,SAAS,KAAK,GAE9B,IAAI,CAAC,EAAE,IAAI,KAAK,GACd,OAAO;CAGX,OAAO;AACT;AAEA,SAAgB,MAAS,GAAG,MAAoC;CAC9D,MAAM,yBAAS,IAAI,IAAO;CAC1B,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAAW,IAAI,KAAK;EAC1B,KACE,IAAI,EAAC,MAAM,UAAS,SAAS,KAAK,GAClC,CAAC,MACD,CAAC,MAAM,SAAS,SAAS,KAAK,GAE9B,OAAO,IAAI,KAAK;CAEpB;CACA,OAAO;AACT;AAEA,SAAgB,aACd,GACA,GACQ;CACR,MAAM,yBAAS,IAAI,IAAO;CAC1B,IAAI,EAAE,OAAO,EAAE,MAAM;EAEnB,MAAM,OAAO;EACb,IAAI;EACJ,IAAI;CACN;CACA,MAAM,WAAW,EAAE,KAAK;CACxB,KACE,IAAI,EAAC,MAAM,UAAS,SAAS,KAAK,GAClC,CAAC,MACD,CAAC,MAAM,SAAS,SAAS,KAAK,GAE9B,IAAI,EAAE,IAAI,KAAK,GACb,OAAO,IAAI,KAAK;CAGpB,OAAO;AACT;;;;AAKA,SAAgB,WACd,GACA,GACQ;CACR,MAAM,yBAAS,IAAI,IAAO;CAC1B,MAAM,WAAW,EAAE,KAAK;CACxB,KACE,IAAI,EAAC,MAAM,UAAS,SAAS,KAAK,GAClC,CAAC,MACD,CAAC,MAAM,SAAS,SAAS,KAAK,GAE9B,IAAI,CAAC,EAAE,IAAI,KAAK,GACd,OAAO,IAAI,KAAK;CAGpB,OAAO;AACT;AAEA,SAAgB,qBACd,GACA,GACgC;CAChC,MAAM,wBAAQ,IAAI,IAAO;CACzB,MAAM,YAAY,EAAE,KAAK;CACzB,KACE,IAAI,EAAC,MAAM,UAAS,UAAU,KAAK,GACnC,CAAC,MACD,CAAC,MAAM,SAAS,UAAU,KAAK,GAE/B,MAAM,IAAI,KAAK;CAGjB,MAAM,wBAAQ,IAAI,IAAO;CACzB,MAAM,YAAY,EAAE,KAAK;CACzB,KACE,IAAI,EAAC,MAAM,UAAS,UAAU,KAAK,GACnC,CAAC,MACD,CAAC,MAAM,SAAS,UAAU,KAAK,GAE/B,IAAI,EAAE,IAAI,KAAK,GAAG;EAChB,MAAM,OAAO,KAAK;EAClB,MAAM,OAAO,KAAK;CACpB,OACE,MAAM,IAAI,KAAK;CAGnB,OAAO,CAAC,OAAO,KAAK;AACtB"}
@@ -1,6 +1,6 @@
1
1
  var package_default = {
2
2
  name: "@rocicorp/zero",
3
- version: "1.11.0-canary.41",
3
+ version: "1.11.0-canary.43",
4
4
  description: "Zero is a web framework for serverless web development.",
5
5
  homepage: "https://zero.rocicorp.dev",
6
6
  bugs: { "url": "https://bugs.rocicorp.dev" },
@@ -1 +1 @@
1
- {"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.11.0-canary.41\",\n \"description\": \"Zero is a web framework for serverless web development.\",\n \"homepage\": \"https://zero.rocicorp.dev\",\n \"bugs\": {\n \"url\": \"https://bugs.rocicorp.dev\"\n },\n \"license\": \"Apache-2.0\",\n \"author\": \"Rocicorp, Inc.\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/rocicorp/mono.git\",\n \"directory\": \"packages/zero\"\n },\n \"bin\": {\n \"analyze-query\": \"./out/zero/src/analyze-query.js\",\n \"ast-to-zql\": \"./out/zero/src/ast-to-zql.js\",\n \"transform-query\": \"./out/zero/src/transform-query.js\",\n \"zero-build-schema\": \"./out/zero/src/build-schema.js\",\n \"zero-cache\": \"./out/zero/src/cli.js\",\n \"zero-cache-dev\": \"./out/zero/src/zero-cache-dev.js\",\n \"zero-deploy-permissions\": \"./out/zero/src/deploy-permissions.js\",\n \"zero-out\": \"./out/zero/src/zero-out.js\"\n },\n \"files\": [\n \"out\",\n \"!*.tsbuildinfo\"\n ],\n \"type\": \"module\",\n \"main\": \"out/zero/src/zero.js\",\n \"module\": \"out/zero/src/zero.js\",\n \"types\": \"out/zero/src/zero.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./out/zero/src/zero.d.ts\",\n \"default\": \"./out/zero/src/zero.js\"\n },\n \"./analyze\": {\n \"types\": \"./out/zero/src/analyze.d.ts\",\n \"default\": \"./out/zero/src/analyze.js\"\n },\n \"./bindings\": {\n \"types\": \"./out/zero/src/bindings.d.ts\",\n \"default\": \"./out/zero/src/bindings.js\"\n },\n \"./change-protocol/v0\": {\n \"types\": \"./out/zero/src/change-protocol/v0.d.ts\",\n \"default\": \"./out/zero/src/change-protocol/v0.js\"\n },\n \"./expo-sqlite\": {\n \"types\": \"./out/zero/src/expo-sqlite.d.ts\",\n \"default\": \"./out/zero/src/expo-sqlite.js\"\n },\n \"./op-sqlite\": {\n \"types\": \"./out/zero/src/op-sqlite.d.ts\",\n \"default\": \"./out/zero/src/op-sqlite.js\"\n },\n \"./pg\": {\n \"types\": \"./out/zero/src/pg.d.ts\",\n \"default\": \"./out/zero/src/pg.js\"\n },\n \"./react\": {\n \"types\": \"./out/zero/src/react.d.ts\",\n \"default\": \"./out/zero/src/react.js\"\n },\n \"./react-native\": {\n \"types\": \"./out/zero/src/react-native.d.ts\",\n \"default\": \"./out/zero/src/react-native.js\"\n },\n \"./server\": {\n \"types\": \"./out/zero/src/server.d.ts\",\n \"default\": \"./out/zero/src/server.js\"\n },\n \"./server/adapters/drizzle\": {\n \"types\": \"./out/zero/src/adapters/drizzle.d.ts\",\n \"default\": \"./out/zero/src/adapters/drizzle.js\"\n },\n \"./server/adapters/kysely\": {\n \"types\": \"./out/zero/src/adapters/kysely.d.ts\",\n \"default\": \"./out/zero/src/adapters/kysely.js\"\n },\n \"./server/adapters/prisma\": {\n \"types\": \"./out/zero/src/adapters/prisma.d.ts\",\n \"default\": \"./out/zero/src/adapters/prisma.js\"\n },\n \"./server/adapters/pg\": {\n \"types\": \"./out/zero/src/adapters/pg.d.ts\",\n \"default\": \"./out/zero/src/adapters/pg.js\"\n },\n \"./server/adapters/postgresjs\": {\n \"types\": \"./out/zero/src/adapters/postgresjs.d.ts\",\n \"default\": \"./out/zero/src/adapters/postgresjs.js\"\n },\n \"./solid\": {\n \"types\": \"./out/zero/src/solid.d.ts\",\n \"default\": \"./out/zero/src/solid.js\"\n },\n \"./sqlite\": {\n \"types\": \"./out/zero/src/sqlite.d.ts\",\n \"default\": \"./out/zero/src/sqlite.js\"\n },\n \"./zqlite\": {\n \"types\": \"./out/zero/src/zqlite.d.ts\",\n \"default\": \"./out/zero/src/zqlite.js\"\n }\n },\n \"scripts\": {\n \"build\": \"node --experimental-strip-types --no-warnings tool/build.ts\",\n \"build:watch\": \"node --experimental-strip-types --no-warnings tool/build.ts --watch\",\n \"check-types\": \"tsc -p tsconfig.client.json && tsc -p tsconfig.server.json\",\n \"check-types:client:watch\": \"tsc -p tsconfig.client.json --watch\",\n \"check-types:server:watch\": \"tsc -p tsconfig.server.json --watch\",\n \"format\": \"oxfmt .\",\n \"check-format\": \"oxfmt --check .\",\n \"lint\": \"oxlint --quiet --config ../../oxlint.config.ts src/\",\n \"docs\": \"node --experimental-strip-types --no-warnings tool/generate-docs.ts\",\n \"docs:server\": \"node --watch --experimental-strip-types --no-warnings tool/generate-docs.ts --server\",\n \"fmt\": \"oxfmt .\",\n \"check-fmt\": \"oxfmt --check .\"\n },\n \"dependencies\": {\n \"@badrap/valita\": \"0.5.5\",\n \"@databases/escape-identifier\": \"^1.0.3\",\n \"@databases/sql\": \"^3.3.0\",\n \"@dotenvx/dotenvx\": \"^1.39.0\",\n \"@drdgvhbh/postgres-error-codes\": \"^0.0.6\",\n \"@fastify/cors\": \"^10.0.0\",\n \"@fastify/websocket\": \"^11.0.0\",\n \"@google-cloud/precise-date\": \"^4.0.0\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/api-logs\": \"^0.218.0\",\n \"@opentelemetry/auto-instrumentations-node\": \"^0.76.0\",\n \"@opentelemetry/exporter-metrics-otlp-http\": \"^0.218.0\",\n \"@opentelemetry/resources\": \"^2.7.1\",\n \"@opentelemetry/sdk-metrics\": \"^2.7.1\",\n \"@opentelemetry/sdk-node\": \"^0.218.0\",\n \"@opentelemetry/sdk-trace-node\": \"^2.7.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.41.1\",\n \"@postgresql-typed/oids\": \"^0.2.0\",\n \"@rocicorp/lock\": \"^1.0.4\",\n \"@rocicorp/logger\": \"^6.1.0\",\n \"@rocicorp/resolver\": \"^1.0.2\",\n \"@rocicorp/zero-sqlite3\": \"^1.1.5\",\n \"@standard-schema/spec\": \"^1.0.0\",\n \"@types/basic-auth\": \"^1.1.8\",\n \"@types/ws\": \"^8.5.12\",\n \"basic-auth\": \"^2.0.1\",\n \"chalk-template\": \"^1.1.0\",\n \"chokidar\": \"^4.0.1\",\n \"cloudevents\": \"^10.0.0\",\n \"command-line-args\": \"^6.0.1\",\n \"command-line-usage\": \"^7.0.3\",\n \"compare-utf8\": \"^0.2.3\",\n \"defu\": \"^6.1.4\",\n \"eventemitter3\": \"^5.0.1\",\n \"fastify\": \"^5.0.0\",\n \"is-in-subnet\": \"^4.0.1\",\n \"jose\": \"^5.9.3\",\n \"json-custom-numbers\": \"^3.1.1\",\n \"kasi\": \"^1.1.0\",\n \"nanoid\": \"^5.1.2\",\n \"oxfmt\": \"^0.65.0\",\n \"parse-prometheus-text-format\": \"^1.1.1\",\n \"pg-format\": \"npm:pg-format-fix@^1.0.5\",\n \"postgres\": \"3.4.7\",\n \"semver\": \"^7.5.4\",\n \"url-pattern\": \"^1.0.3\",\n \"urlpattern-polyfill\": \"^10.1.0\",\n \"ws\": \"^8.18.1\"\n },\n \"devDependencies\": {\n \"@op-engineering/op-sqlite\": \">=15\",\n \"analyze-query\": \"workspace:*\",\n \"ast-to-zql\": \"workspace:*\",\n \"expo-sqlite\": \">=15\",\n \"oxc-parser\": \"^0.129.0\",\n \"replicache\": \"workspace:*\",\n \"shared\": \"workspace:*\",\n \"syncpack\": \"^14.3.0\",\n \"typedoc\": \"^0.28.17\",\n \"typescript\": \"~7.0.2\",\n \"vite\": \"^8.0.13\",\n \"vitest\": \"^5.0.0\",\n \"zero-cache\": \"workspace:*\",\n \"zero-client\": \"workspace:*\",\n \"zero-pg\": \"workspace:*\",\n \"zero-react\": \"workspace:*\",\n \"zero-server\": \"workspace:*\",\n \"zero-solid\": \"workspace:*\",\n \"zqlite\": \"workspace:*\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"packageManager\": \"pnpm@11.11.0\"\n}\n"],"mappings":""}
1
+ {"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.11.0-canary.43\",\n \"description\": \"Zero is a web framework for serverless web development.\",\n \"homepage\": \"https://zero.rocicorp.dev\",\n \"bugs\": {\n \"url\": \"https://bugs.rocicorp.dev\"\n },\n \"license\": \"Apache-2.0\",\n \"author\": \"Rocicorp, Inc.\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/rocicorp/mono.git\",\n \"directory\": \"packages/zero\"\n },\n \"bin\": {\n \"analyze-query\": \"./out/zero/src/analyze-query.js\",\n \"ast-to-zql\": \"./out/zero/src/ast-to-zql.js\",\n \"transform-query\": \"./out/zero/src/transform-query.js\",\n \"zero-build-schema\": \"./out/zero/src/build-schema.js\",\n \"zero-cache\": \"./out/zero/src/cli.js\",\n \"zero-cache-dev\": \"./out/zero/src/zero-cache-dev.js\",\n \"zero-deploy-permissions\": \"./out/zero/src/deploy-permissions.js\",\n \"zero-out\": \"./out/zero/src/zero-out.js\"\n },\n \"files\": [\n \"out\",\n \"!*.tsbuildinfo\"\n ],\n \"type\": \"module\",\n \"main\": \"out/zero/src/zero.js\",\n \"module\": \"out/zero/src/zero.js\",\n \"types\": \"out/zero/src/zero.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./out/zero/src/zero.d.ts\",\n \"default\": \"./out/zero/src/zero.js\"\n },\n \"./analyze\": {\n \"types\": \"./out/zero/src/analyze.d.ts\",\n \"default\": \"./out/zero/src/analyze.js\"\n },\n \"./bindings\": {\n \"types\": \"./out/zero/src/bindings.d.ts\",\n \"default\": \"./out/zero/src/bindings.js\"\n },\n \"./change-protocol/v0\": {\n \"types\": \"./out/zero/src/change-protocol/v0.d.ts\",\n \"default\": \"./out/zero/src/change-protocol/v0.js\"\n },\n \"./expo-sqlite\": {\n \"types\": \"./out/zero/src/expo-sqlite.d.ts\",\n \"default\": \"./out/zero/src/expo-sqlite.js\"\n },\n \"./op-sqlite\": {\n \"types\": \"./out/zero/src/op-sqlite.d.ts\",\n \"default\": \"./out/zero/src/op-sqlite.js\"\n },\n \"./pg\": {\n \"types\": \"./out/zero/src/pg.d.ts\",\n \"default\": \"./out/zero/src/pg.js\"\n },\n \"./react\": {\n \"types\": \"./out/zero/src/react.d.ts\",\n \"default\": \"./out/zero/src/react.js\"\n },\n \"./react-native\": {\n \"types\": \"./out/zero/src/react-native.d.ts\",\n \"default\": \"./out/zero/src/react-native.js\"\n },\n \"./server\": {\n \"types\": \"./out/zero/src/server.d.ts\",\n \"default\": \"./out/zero/src/server.js\"\n },\n \"./server/adapters/drizzle\": {\n \"types\": \"./out/zero/src/adapters/drizzle.d.ts\",\n \"default\": \"./out/zero/src/adapters/drizzle.js\"\n },\n \"./server/adapters/kysely\": {\n \"types\": \"./out/zero/src/adapters/kysely.d.ts\",\n \"default\": \"./out/zero/src/adapters/kysely.js\"\n },\n \"./server/adapters/prisma\": {\n \"types\": \"./out/zero/src/adapters/prisma.d.ts\",\n \"default\": \"./out/zero/src/adapters/prisma.js\"\n },\n \"./server/adapters/pg\": {\n \"types\": \"./out/zero/src/adapters/pg.d.ts\",\n \"default\": \"./out/zero/src/adapters/pg.js\"\n },\n \"./server/adapters/postgresjs\": {\n \"types\": \"./out/zero/src/adapters/postgresjs.d.ts\",\n \"default\": \"./out/zero/src/adapters/postgresjs.js\"\n },\n \"./solid\": {\n \"types\": \"./out/zero/src/solid.d.ts\",\n \"default\": \"./out/zero/src/solid.js\"\n },\n \"./sqlite\": {\n \"types\": \"./out/zero/src/sqlite.d.ts\",\n \"default\": \"./out/zero/src/sqlite.js\"\n },\n \"./zqlite\": {\n \"types\": \"./out/zero/src/zqlite.d.ts\",\n \"default\": \"./out/zero/src/zqlite.js\"\n }\n },\n \"scripts\": {\n \"build\": \"node --experimental-strip-types --no-warnings tool/build.ts\",\n \"build:watch\": \"node --experimental-strip-types --no-warnings tool/build.ts --watch\",\n \"check-types\": \"tsc -p tsconfig.client.json && tsc -p tsconfig.server.json\",\n \"check-types:client:watch\": \"tsc -p tsconfig.client.json --watch\",\n \"check-types:server:watch\": \"tsc -p tsconfig.server.json --watch\",\n \"format\": \"oxfmt .\",\n \"check-format\": \"oxfmt --check .\",\n \"lint\": \"oxlint --quiet --config ../../oxlint.config.ts src/\",\n \"docs\": \"node --experimental-strip-types --no-warnings tool/generate-docs.ts\",\n \"docs:server\": \"node --watch --experimental-strip-types --no-warnings tool/generate-docs.ts --server\",\n \"fmt\": \"oxfmt .\",\n \"check-fmt\": \"oxfmt --check .\"\n },\n \"dependencies\": {\n \"@badrap/valita\": \"0.5.5\",\n \"@databases/escape-identifier\": \"^1.0.3\",\n \"@databases/sql\": \"^3.3.0\",\n \"@dotenvx/dotenvx\": \"^1.39.0\",\n \"@drdgvhbh/postgres-error-codes\": \"^0.0.6\",\n \"@fastify/cors\": \"^10.0.0\",\n \"@fastify/websocket\": \"^11.0.0\",\n \"@google-cloud/precise-date\": \"^4.0.0\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/api-logs\": \"^0.218.0\",\n \"@opentelemetry/auto-instrumentations-node\": \"^0.76.0\",\n \"@opentelemetry/exporter-metrics-otlp-http\": \"^0.218.0\",\n \"@opentelemetry/resources\": \"^2.7.1\",\n \"@opentelemetry/sdk-metrics\": \"^2.7.1\",\n \"@opentelemetry/sdk-node\": \"^0.218.0\",\n \"@opentelemetry/sdk-trace-node\": \"^2.7.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.41.1\",\n \"@postgresql-typed/oids\": \"^0.2.0\",\n \"@rocicorp/lock\": \"^1.0.4\",\n \"@rocicorp/logger\": \"^6.1.0\",\n \"@rocicorp/resolver\": \"^1.0.2\",\n \"@rocicorp/zero-sqlite3\": \"^1.1.5\",\n \"@standard-schema/spec\": \"^1.0.0\",\n \"@types/basic-auth\": \"^1.1.8\",\n \"@types/ws\": \"^8.5.12\",\n \"basic-auth\": \"^2.0.1\",\n \"chalk-template\": \"^1.1.0\",\n \"chokidar\": \"^4.0.1\",\n \"cloudevents\": \"^10.0.0\",\n \"command-line-args\": \"^6.0.1\",\n \"command-line-usage\": \"^7.0.3\",\n \"compare-utf8\": \"^0.2.3\",\n \"defu\": \"^6.1.4\",\n \"eventemitter3\": \"^5.0.1\",\n \"fastify\": \"^5.0.0\",\n \"is-in-subnet\": \"^4.0.1\",\n \"jose\": \"^5.9.3\",\n \"json-custom-numbers\": \"^3.1.1\",\n \"kasi\": \"^1.1.0\",\n \"nanoid\": \"^5.1.2\",\n \"oxfmt\": \"^0.65.0\",\n \"parse-prometheus-text-format\": \"^1.1.1\",\n \"pg-format\": \"npm:pg-format-fix@^1.0.5\",\n \"postgres\": \"3.4.7\",\n \"semver\": \"^7.5.4\",\n \"url-pattern\": \"^1.0.3\",\n \"urlpattern-polyfill\": \"^10.1.0\",\n \"ws\": \"^8.18.1\"\n },\n \"devDependencies\": {\n \"@op-engineering/op-sqlite\": \">=15\",\n \"analyze-query\": \"workspace:*\",\n \"ast-to-zql\": \"workspace:*\",\n \"expo-sqlite\": \">=15\",\n \"oxc-parser\": \"^0.129.0\",\n \"replicache\": \"workspace:*\",\n \"shared\": \"workspace:*\",\n \"syncpack\": \"^14.3.0\",\n \"typedoc\": \"^0.28.17\",\n \"typescript\": \"~7.0.2\",\n \"vite\": \"^8.0.13\",\n \"vitest\": \"^5.0.0\",\n \"zero-cache\": \"workspace:*\",\n \"zero-client\": \"workspace:*\",\n \"zero-pg\": \"workspace:*\",\n \"zero-react\": \"workspace:*\",\n \"zero-server\": \"workspace:*\",\n \"zero-solid\": \"workspace:*\",\n \"zqlite\": \"workspace:*\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"packageManager\": \"pnpm@11.11.0\"\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"change-streamer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"names":[],"mappings":"AA4CA,OAAO,EAML,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAgB/B,wBAA8B,SAAS,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAoaf"}
1
+ {"version":3,"file":"change-streamer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"names":[],"mappings":"AA6CA,OAAO,EAML,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAgB/B,wBAA8B,SAAS,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAuaf"}
@@ -22,6 +22,7 @@ import { ReplicationStatusPublisher, replicationStatusError } from "../services/
22
22
  import { changeLogFileName, deleteChangeLogDB } from "../services/replicator/change-log-db.js";
23
23
  import { deleteLitestreamMetaDir } from "../services/litestream/commands.js";
24
24
  import { initializeCustomChangeSource } from "../services/change-source/custom/change-source.js";
25
+ import { warnIfPostgresUnpatched } from "../types/pg-patch.js";
25
26
  import { initializePostgresChangeSource } from "../services/change-source/pg/change-source-init.js";
26
27
  import { createBackupCleanupMonitor } from "../services/change-streamer/backup-cleanup-monitor-factory.js";
27
28
  import { ChangeStreamerHttpServer } from "../services/change-streamer/change-streamer-http.js";
@@ -50,6 +51,7 @@ async function runWorker(parent, env, ...argv) {
50
51
  dbPath: replica.file
51
52
  }, sqliteCorruptionChecks);
52
53
  initEventSink(lc, config);
54
+ if (upstream.type === "pg") warnIfPostgresUnpatched(lc);
53
55
  const changeDB = await connectPgClient(lc, change.db, "change-streamer-init", { max: 5 }, { sendStringAsJson: true });
54
56
  warmupConnections(lc, changeDB, "change").catch(() => {});
55
57
  const shard = getShardConfig(config);
@@ -1 +1 @@
1
- {"version":3,"file":"change-streamer.js","names":[],"sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {DatabaseInitError} from '../../../zqlite/src/db.ts';\nimport {getServerContext} from '../config/server-context.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {deleteLiteDB} from '../db/delete-lite-db.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink, publishCriticalEvent} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {InitCleanup} from '../services/change-source/common/init-cleanup.ts';\nimport type {PgChangeLogPurgeLocker} from '../services/change-source/common/replica-restore.ts';\nimport {initializeCustomChangeSource} from '../services/change-source/custom/change-source.ts';\nimport {initializePostgresChangeSource} from '../services/change-source/pg/change-source-init.ts';\nimport {createBackupCleanupMonitor} from '../services/change-streamer/backup-cleanup-monitor-factory.ts';\nimport {ChangeStreamerHttpServer} from '../services/change-streamer/change-streamer-http.ts';\nimport {initializeStreamer} from '../services/change-streamer/change-streamer-service.ts';\nimport type {ChangeStreamerService} from '../services/change-streamer/change-streamer.ts';\nimport {DEFAULT_READINESS_GRACE_PERIOD_MS} from '../services/change-streamer/restore-admission.ts';\nimport {initChangeStreamerSchema} from '../services/change-streamer/schema/init.ts';\nimport {AutoResetSignal} from '../services/change-streamer/schema/tables.ts';\nimport {\n PurgeLocker,\n type PurgeLock,\n} from '../services/change-streamer/storer.ts';\nimport {\n exitAfter,\n runUntilKilled,\n SupportingProcesses,\n} from '../services/life-cycle.ts';\nimport {deleteLitestreamMetaDir} from '../services/litestream/commands.ts';\nimport {\n changeLogFileName,\n deleteChangeLogDB,\n} from '../services/replicator/change-log-db.ts';\nimport {\n replicationStatusError,\n ReplicationStatusPublisher,\n} from '../services/replicator/replication-status.ts';\nimport {sqliteFileBytes} from '../services/replicator/sqlite-change-log-observability.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n broadcastWorker,\n childWorker,\n parentWorker,\n singleProcessMode,\n type ProfileResponseMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport type {ReplicaFileMode} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {REPLICATOR_URL} from './worker-urls.ts';\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\n// Errors thrown once the services are running are reported by the services\n// themselves (e.g. as 'Replicating' status events), and are thus not\n// published as 'Initializing' errors.\nlet running = false;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n installProfileHandler(parent, 'change-streamer');\n const config = getNormalizedZeroConfig({env, argv});\n const {\n taskID,\n changeStreamer: {\n port,\n address,\n protocol,\n startupDelayMs,\n backPressureLimitHeapProportion,\n flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodSeconds,\n maxConcurrentSwitchoverRestores,\n pgChangeLogEnabled,\n sqliteChangeLogMode,\n sqliteChangeLogReadPercent,\n sqliteChangeLogColdReadPercent,\n sqliteChangeLogComparePercent,\n sqliteChangeLogRetentionMs,\n sqliteChangeLogReadBatchRows,\n sqliteChangeLogPurgeBatchRows,\n sqliteChangeLogBarrierTimeoutMs,\n },\n autoReset,\n replicationLag,\n litestream,\n upstream,\n change,\n replica,\n initialSync,\n keepaliveTimeoutMs,\n sqliteCorruptionChecks,\n } = config;\n\n startOtelAuto(\n createLogContext(config, 'change-streamer', 0, false),\n 'change-streamer',\n 0,\n );\n lc = createLogContext(config, 'change-streamer');\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'change-streamer replica',\n dbPath: replica.file,\n },\n sqliteCorruptionChecks,\n );\n initEventSink(lc, config);\n\n // Startup-time client used for for change-streamer initialization\n // and handoff / takeover. Steady-state clients are managed by the\n // change-streamer (Storer) implementation.\n const changeDB = await connectPgClient(\n lc,\n change.db,\n 'change-streamer-init',\n {max: 5},\n {sendStringAsJson: true},\n );\n void warmupConnections(lc, changeDB, 'change').catch(() => {});\n\n const shard = getShardConfig(config);\n\n // Ensure the change DB schema is initialized/up-to-date.\n await initChangeStreamerSchema(lc, changeDB, shard);\n\n let purgeLock = null as PurgeLock | null;\n let changeStreamer: ChangeStreamerService | undefined;\n let backupURL: string | undefined;\n\n const context = getServerContext(config);\n const sqliteChangeLogEnabled = sqliteChangeLogMode !== 'off';\n // The modes are cumulative, so `serve` implies `compare`.\n const sqliteChangeLogComparing =\n sqliteChangeLogMode === 'compare' || sqliteChangeLogMode === 'serve';\n if (sqliteChangeLogEnabled) {\n const changeLogFile = changeLogFileName(replica.file);\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'change-streamer change-log',\n dbPath: changeLogFile,\n },\n sqliteCorruptionChecks,\n );\n // The log's bytes are accounted for in the process that writes them. This\n // is local disk only — the log is excluded from the litestream backup — and\n // it is a whole-database total, because a table that left the replica left\n // through its wal: the replica's footprint is what shrank and this is where\n // it went.\n getOrCreateGauge('replica', 'sqlite_change_log.file_bytes', {\n description:\n `The SQLite change log's total on-disk footprint: its main db file ` +\n `plus its wal sidecars.`,\n unit: 'By',\n }).addCallback(async o =>\n o.observe(await sqliteFileBytes(lc, changeLogFile)),\n );\n }\n\n let waitForFirstBackupBeforeServing = false;\n let newBackupLineage = false;\n for (const first of [true, false]) {\n // Resources acquired by an initialization attempt (e.g. a claimed\n // replication slot, the purge lock) to be released if the attempt fails.\n const cleanup = new InitCleanup(lc);\n // When restoring from litestream, a lock is acquired to prevent change-log\n // purges. This ensures that (this) change-streamer will be able to resume\n // from the backup. The lock is acquired by the change source initialization\n // just before restoring (i.e. after a replication slot is created, if\n // applicable), and released once this change-streamer takes over the\n // change-log.\n const acquirePurgeLock: PgChangeLogPurgeLocker | undefined =\n pgChangeLogEnabled && litestream.backupURL && litestream.executable\n ? async (slotWatermark?: string) => {\n const lock = await new PurgeLocker(lc, shard, changeDB).acquire(\n slotWatermark,\n );\n purgeLock = lock === 'behind-slot' ? null : lock;\n if (purgeLock) {\n const acquired = purgeLock;\n cleanup.onFailure('purge lock', () => {\n purgeLock = null;\n return acquired.release();\n });\n }\n return lock;\n }\n : undefined;\n\n const restoreOptions = {litestream, acquirePurgeLock, cleanup};\n try {\n // Note: This performs initial sync of the replica if necessary.\n const {\n pgReplicationEpoch: epoch,\n pgReplicationSlotPerReplica: slotPerReplica,\n pgResumeOrphanedSlotGracePeriodMs: inactiveReplicaGracePeriodMs,\n } = upstream;\n const {\n changeSource,\n subscriptionState,\n destinationBackupURL,\n replicaID,\n waitForBackupBeforeServing,\n newBackupLineage: initNewBackupLineage,\n pgChangeLogBehindSlot,\n } = upstream.type === 'pg'\n ? await initializePostgresChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n {\n ...initialSync,\n replicationSlotFailover: upstream.pgReplicationSlotFailover,\n installPartialIndexTriggers: upstream.pgPartialIndexTriggers,\n },\n context,\n replicationLag.reportIntervalMs,\n restoreOptions,\n {\n epoch,\n slotPerReplica,\n inactiveReplicaGracePeriodMs,\n backupV5: litestream.backupUsingV5,\n pgChangeLog: pgChangeLogEnabled,\n },\n upstream.pgStreamInboundTimeoutMs,\n )\n : await initializeCustomChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n context,\n restoreOptions,\n );\n\n const replicationStatusPublisher =\n ReplicationStatusPublisher.forReplicaFile(replica.file);\n\n changeStreamer = await initializeStreamer(\n lc,\n shard,\n taskID,\n address,\n protocol,\n changeDB,\n changeSource,\n replicationStatusPublisher,\n subscriptionState,\n destinationBackupURL\n ? {\n backupURL: destinationBackupURL,\n litestreamVersion: litestream.backupUsingV5 ? 'v5' : 'legacy',\n replicaFile: replica.file,\n }\n : null,\n purgeLock,\n autoReset ?? false,\n {\n pgChangeLogEnabled,\n pgChangeLogBehindSlot: pgChangeLogBehindSlot ?? false,\n backPressureLimitHeapProportion,\n flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodMs:\n flowControlSlowSubscriberGracePeriodSeconds > 0\n ? flowControlSlowSubscriberGracePeriodSeconds * 1000\n : undefined,\n maxConcurrentSwitchoverRestores,\n // When lag reporting is enabled, a restored view-syncer waits for\n // the next lag report (sent at an interval with ~1 second of\n // jitter) before becoming ready.\n switchoverRestoreReadinessGracePeriodMs:\n (replicationLag.reportIntervalMs > 0\n ? replicationLag.reportIntervalMs + 1000\n : 0) + DEFAULT_READINESS_GRACE_PERIOD_MS,\n statementTimeoutMs: change.statementTimeoutMs,\n changeLogBatchSize: change.logBatchSize,\n sqliteCatchup: {\n changeLogFile: changeLogFileName(replica.file),\n readBatchRows: sqliteChangeLogReadBatchRows,\n barrierTimeoutMs: sqliteChangeLogBarrierTimeoutMs,\n },\n // The presence of these options is the writer's gate. This process\n // performs the restore and the initial sync, so the replica's\n // identity is in hand at the moment the log is opened.\n sqliteChangeLogWriter: sqliteChangeLogEnabled\n ? {\n replicaFile: replica.file,\n identity: {\n // RMv2 supplies the epoch; until then the generation and the\n // replica ID are the whole of the identity.\n epoch: null,\n generation: subscriptionState.replicaVersion,\n replicaID,\n },\n }\n : undefined,\n // The purge scheduler shares the writer's gate -- mode, not the\n // read path -- because `write` mode, with no read selector at all,\n // is the configuration it ships in.\n sqliteChangeLogPurge: sqliteChangeLogEnabled\n ? {\n retentionMs: sqliteChangeLogRetentionMs,\n batchRows: sqliteChangeLogPurgeBatchRows,\n }\n : undefined,\n // Compare mode runs both advisory checks. Postgres remains authoritative.\n sqliteChangeLogCompare:\n pgChangeLogEnabled && sqliteChangeLogComparing\n ? {\n replicaFile: replica.file,\n comparePercent: sqliteChangeLogComparePercent,\n retentionMs: sqliteChangeLogRetentionMs,\n readBatchRows: sqliteChangeLogReadBatchRows,\n }\n : undefined,\n // Slice 11 lands dark by default: serve mode constructs the stable\n // router, while readPercent=0 keeps every catchup on PG and emits\n // eligibility metrics before any canary traffic is enabled.\n sqliteChangeLogServe:\n sqliteChangeLogMode === 'serve'\n ? {\n readPercent: sqliteChangeLogReadPercent,\n coldReadPercent: sqliteChangeLogColdReadPercent,\n retentionMs: sqliteChangeLogRetentionMs,\n }\n : undefined,\n },\n setTimeout,\n );\n backupURL = destinationBackupURL;\n waitForFirstBackupBeforeServing = waitForBackupBeforeServing;\n newBackupLineage = initNewBackupLineage;\n break;\n } catch (e) {\n // Release the resources acquired by the failed attempt, e.g.:\n // * The claimed replication slot, which would otherwise remain active\n // indefinitely, preventing a retry from resuming (or forking) it.\n // * The purge lock. This is safe because the purge lock exists to\n // preserve change-log entries so the new change-streamer can resume\n // from the backup replica's watermark. An AutoResetSignal means we\n // can't resume from the backup replica (e.g. its replication slot is\n // gone), so the change-log entries the lock was protecting are no\n // longer needed. The retry performs a fresh initial sync with a new\n // replication slot, independent of the old change-log. Releasing is\n // also necessary to avoid a self-deadlock when CHANGE_DB ==\n // UPSTREAM_DB: CREATE_REPLICATION_SLOT waits for all older\n // transactions to finish, including this lock's open transaction.\n await cleanup.release();\n if (first && e instanceof AutoResetSignal) {\n lc.warn?.(`resetting replica ${replica.file}`, e);\n // TODO: Make deleteLiteDB work with litestream. It will probably have to be\n // a semantic wipe instead of a file delete.\n deleteLiteDB(replica.file);\n // The change log carries the identity of the replica it was written\n // beside, and the retry performs a fresh initial sync with a new\n // replicaVersion. Reconciliation would catch that on its own (as\n // 'identity-mismatch') but only after the writer opened a file that is\n // known here to be garbage.\n deleteChangeLogDB(replica.file);\n continue; // execute again with a fresh initial-sync\n }\n if (e instanceof DatabaseInitError) {\n throw new Error(\n `Cannot open ZERO_REPLICA_FILE at \"${replica.file}\". Please check that the path is valid.`,\n {cause: e},\n );\n }\n throw e;\n }\n }\n // impossible: upstream must have advanced in order for replication to be stuck.\n assert(changeStreamer, `resetting replica did not advance replicaVersion`);\n\n // The backup-replicator is stopped with the other services, i.e. after the\n // ChangeStreamerHttpServer has drained its subscribers.\n const processes = new SupportingProcesses(lc, parent);\n const profileSubWorkers: Worker[] = [];\n const {promise: replicatorReady, resolve} = resolver();\n if (!backupURL) {\n resolve(); // No backup replicator to wait for, so resolve immediately.\n } else {\n lc.info?.('setting up backup to', backupURL);\n config.litestream.backupURLOverride = backupURL;\n if (newBackupLineage) {\n deleteLitestreamMetaDir(replica.file);\n }\n // Start a backup replicator, which starts up the corresponding\n // litestream backup process.\n const backupReplicator = processes\n .add(\n childWorker(\n REPLICATOR_URL,\n {...env, ZERO_LITESTREAM_BACKUP_URL_OVERRIDE: backupURL},\n 'backup' satisfies ReplicaFileMode,\n ),\n 'backup-replicator',\n )\n .onceMessageType('ready', () => resolve());\n profileSubWorkers.push(backupReplicator);\n // Relay profileResponse messages from backup-replicator up to parent\n backupReplicator.onMessageType<ProfileResponseMessage>(\n 'profileResponse',\n res => parent.send(['profileResponse', res]),\n );\n }\n\n const backupMonitor = createBackupCleanupMonitor({\n lc,\n config,\n replicaFile: replica.file,\n changeStreamer,\n });\n\n let backupReady = promiseVoid;\n if (waitForFirstBackupBeforeServing) {\n const start = performance.now();\n lc.info?.(`awaiting initial backup ...`);\n\n backupReady = backupMonitor.firstBackupReceived().then(() => {\n const elapsed = performance.now() - start;\n lc.info?.(`initial backup confirmed after ${elapsed.toFixed(2)}ms`);\n });\n }\n\n // In RMv2, readiness additionally waits for the backup-replicator to catch\n // up to the replication stream. This is not done in RMv1 (i.e. when the\n // PG change-log is enabled), in which the change-streamer itself is only\n // started after the readinessGate (plus a startup delay), and thus the\n // backup-replicator cannot catch up before then.\n const readinessGate = pgChangeLogEnabled\n ? backupReady\n : Promise.all([backupReady, replicatorReady]);\n\n // Create the broadcast facade once: each broadcastWorker() adds permanent\n // 'message' forwarders to every sub worker, so creating one per /profz\n // request would leak a forwarder per request.\n const profileWorker =\n profileSubWorkers.length > 0\n ? broadcastWorker(profileSubWorkers)\n : undefined;\n const getProfileWorker = profileWorker\n ? () => Promise.resolve(profileWorker)\n : undefined;\n\n const changeStreamerWebServer = new ChangeStreamerHttpServer(\n lc,\n {\n port,\n keepaliveTimeoutMs,\n // The startup delay is only relevant when taking over the PG change-log\n // (RMv1 and RMv1.5), and is disabled for RMv2.\n startupDelayMs: pgChangeLogEnabled ? startupDelayMs : 0,\n readinessGate,\n config,\n getProfileWorker,\n },\n parent,\n changeStreamer,\n );\n\n void readinessGate.then(() => parent.send(['ready', {ready: true}]));\n\n // Note: The changeStreamer itself is not started here; it is started by the\n // changeStreamerWebServer after a delay to ensure that routing\n // routing elements have registered the server before the\n // change-streamer takes over the replication slot.\n // TODO: Remove this delay and start the changeStreamer normally here once\n // transitioned to RMv2, since changeStreamer startup will no longer\n // disrupt an existing task.\n running = true;\n await runUntilKilled(\n lc,\n parent,\n changeStreamerWebServer,\n backupMonitor,\n processes,\n );\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () =>\n runWorker(\n must(parentWorker),\n process.env,\n ...process.argv.slice(2),\n ).catch(async e => {\n if (!running) {\n await publishCriticalEvent(\n lc,\n replicationStatusError(lc, 'Initializing', e),\n );\n }\n throw e;\n }),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"change-streamer.js","names":[],"sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {DatabaseInitError} from '../../../zqlite/src/db.ts';\nimport {getServerContext} from '../config/server-context.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {deleteLiteDB} from '../db/delete-lite-db.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink, publishCriticalEvent} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {InitCleanup} from '../services/change-source/common/init-cleanup.ts';\nimport type {PgChangeLogPurgeLocker} from '../services/change-source/common/replica-restore.ts';\nimport {initializeCustomChangeSource} from '../services/change-source/custom/change-source.ts';\nimport {initializePostgresChangeSource} from '../services/change-source/pg/change-source-init.ts';\nimport {createBackupCleanupMonitor} from '../services/change-streamer/backup-cleanup-monitor-factory.ts';\nimport {ChangeStreamerHttpServer} from '../services/change-streamer/change-streamer-http.ts';\nimport {initializeStreamer} from '../services/change-streamer/change-streamer-service.ts';\nimport type {ChangeStreamerService} from '../services/change-streamer/change-streamer.ts';\nimport {DEFAULT_READINESS_GRACE_PERIOD_MS} from '../services/change-streamer/restore-admission.ts';\nimport {initChangeStreamerSchema} from '../services/change-streamer/schema/init.ts';\nimport {AutoResetSignal} from '../services/change-streamer/schema/tables.ts';\nimport {\n PurgeLocker,\n type PurgeLock,\n} from '../services/change-streamer/storer.ts';\nimport {\n exitAfter,\n runUntilKilled,\n SupportingProcesses,\n} from '../services/life-cycle.ts';\nimport {deleteLitestreamMetaDir} from '../services/litestream/commands.ts';\nimport {\n changeLogFileName,\n deleteChangeLogDB,\n} from '../services/replicator/change-log-db.ts';\nimport {\n replicationStatusError,\n ReplicationStatusPublisher,\n} from '../services/replicator/replication-status.ts';\nimport {sqliteFileBytes} from '../services/replicator/sqlite-change-log-observability.ts';\nimport {warnIfPostgresUnpatched} from '../types/pg-patch.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n broadcastWorker,\n childWorker,\n parentWorker,\n singleProcessMode,\n type ProfileResponseMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport type {ReplicaFileMode} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {REPLICATOR_URL} from './worker-urls.ts';\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\n// Errors thrown once the services are running are reported by the services\n// themselves (e.g. as 'Replicating' status events), and are thus not\n// published as 'Initializing' errors.\nlet running = false;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n installProfileHandler(parent, 'change-streamer');\n const config = getNormalizedZeroConfig({env, argv});\n const {\n taskID,\n changeStreamer: {\n port,\n address,\n protocol,\n startupDelayMs,\n backPressureLimitHeapProportion,\n flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodSeconds,\n maxConcurrentSwitchoverRestores,\n pgChangeLogEnabled,\n sqliteChangeLogMode,\n sqliteChangeLogReadPercent,\n sqliteChangeLogColdReadPercent,\n sqliteChangeLogComparePercent,\n sqliteChangeLogRetentionMs,\n sqliteChangeLogReadBatchRows,\n sqliteChangeLogPurgeBatchRows,\n sqliteChangeLogBarrierTimeoutMs,\n },\n autoReset,\n replicationLag,\n litestream,\n upstream,\n change,\n replica,\n initialSync,\n keepaliveTimeoutMs,\n sqliteCorruptionChecks,\n } = config;\n\n startOtelAuto(\n createLogContext(config, 'change-streamer', 0, false),\n 'change-streamer',\n 0,\n );\n lc = createLogContext(config, 'change-streamer');\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'change-streamer replica',\n dbPath: replica.file,\n },\n sqliteCorruptionChecks,\n );\n initEventSink(lc, config);\n if (upstream.type === 'pg') {\n warnIfPostgresUnpatched(lc);\n }\n\n // Startup-time client used for for change-streamer initialization\n // and handoff / takeover. Steady-state clients are managed by the\n // change-streamer (Storer) implementation.\n const changeDB = await connectPgClient(\n lc,\n change.db,\n 'change-streamer-init',\n {max: 5},\n {sendStringAsJson: true},\n );\n void warmupConnections(lc, changeDB, 'change').catch(() => {});\n\n const shard = getShardConfig(config);\n\n // Ensure the change DB schema is initialized/up-to-date.\n await initChangeStreamerSchema(lc, changeDB, shard);\n\n let purgeLock = null as PurgeLock | null;\n let changeStreamer: ChangeStreamerService | undefined;\n let backupURL: string | undefined;\n\n const context = getServerContext(config);\n const sqliteChangeLogEnabled = sqliteChangeLogMode !== 'off';\n // The modes are cumulative, so `serve` implies `compare`.\n const sqliteChangeLogComparing =\n sqliteChangeLogMode === 'compare' || sqliteChangeLogMode === 'serve';\n if (sqliteChangeLogEnabled) {\n const changeLogFile = changeLogFileName(replica.file);\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'change-streamer change-log',\n dbPath: changeLogFile,\n },\n sqliteCorruptionChecks,\n );\n // The log's bytes are accounted for in the process that writes them. This\n // is local disk only — the log is excluded from the litestream backup — and\n // it is a whole-database total, because a table that left the replica left\n // through its wal: the replica's footprint is what shrank and this is where\n // it went.\n getOrCreateGauge('replica', 'sqlite_change_log.file_bytes', {\n description:\n `The SQLite change log's total on-disk footprint: its main db file ` +\n `plus its wal sidecars.`,\n unit: 'By',\n }).addCallback(async o =>\n o.observe(await sqliteFileBytes(lc, changeLogFile)),\n );\n }\n\n let waitForFirstBackupBeforeServing = false;\n let newBackupLineage = false;\n for (const first of [true, false]) {\n // Resources acquired by an initialization attempt (e.g. a claimed\n // replication slot, the purge lock) to be released if the attempt fails.\n const cleanup = new InitCleanup(lc);\n // When restoring from litestream, a lock is acquired to prevent change-log\n // purges. This ensures that (this) change-streamer will be able to resume\n // from the backup. The lock is acquired by the change source initialization\n // just before restoring (i.e. after a replication slot is created, if\n // applicable), and released once this change-streamer takes over the\n // change-log.\n const acquirePurgeLock: PgChangeLogPurgeLocker | undefined =\n pgChangeLogEnabled && litestream.backupURL && litestream.executable\n ? async (slotWatermark?: string) => {\n const lock = await new PurgeLocker(lc, shard, changeDB).acquire(\n slotWatermark,\n );\n purgeLock = lock === 'behind-slot' ? null : lock;\n if (purgeLock) {\n const acquired = purgeLock;\n cleanup.onFailure('purge lock', () => {\n purgeLock = null;\n return acquired.release();\n });\n }\n return lock;\n }\n : undefined;\n\n const restoreOptions = {litestream, acquirePurgeLock, cleanup};\n try {\n // Note: This performs initial sync of the replica if necessary.\n const {\n pgReplicationEpoch: epoch,\n pgReplicationSlotPerReplica: slotPerReplica,\n pgResumeOrphanedSlotGracePeriodMs: inactiveReplicaGracePeriodMs,\n } = upstream;\n const {\n changeSource,\n subscriptionState,\n destinationBackupURL,\n replicaID,\n waitForBackupBeforeServing,\n newBackupLineage: initNewBackupLineage,\n pgChangeLogBehindSlot,\n } = upstream.type === 'pg'\n ? await initializePostgresChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n {\n ...initialSync,\n replicationSlotFailover: upstream.pgReplicationSlotFailover,\n installPartialIndexTriggers: upstream.pgPartialIndexTriggers,\n },\n context,\n replicationLag.reportIntervalMs,\n restoreOptions,\n {\n epoch,\n slotPerReplica,\n inactiveReplicaGracePeriodMs,\n backupV5: litestream.backupUsingV5,\n pgChangeLog: pgChangeLogEnabled,\n },\n upstream.pgStreamInboundTimeoutMs,\n )\n : await initializeCustomChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n context,\n restoreOptions,\n );\n\n const replicationStatusPublisher =\n ReplicationStatusPublisher.forReplicaFile(replica.file);\n\n changeStreamer = await initializeStreamer(\n lc,\n shard,\n taskID,\n address,\n protocol,\n changeDB,\n changeSource,\n replicationStatusPublisher,\n subscriptionState,\n destinationBackupURL\n ? {\n backupURL: destinationBackupURL,\n litestreamVersion: litestream.backupUsingV5 ? 'v5' : 'legacy',\n replicaFile: replica.file,\n }\n : null,\n purgeLock,\n autoReset ?? false,\n {\n pgChangeLogEnabled,\n pgChangeLogBehindSlot: pgChangeLogBehindSlot ?? false,\n backPressureLimitHeapProportion,\n flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodMs:\n flowControlSlowSubscriberGracePeriodSeconds > 0\n ? flowControlSlowSubscriberGracePeriodSeconds * 1000\n : undefined,\n maxConcurrentSwitchoverRestores,\n // When lag reporting is enabled, a restored view-syncer waits for\n // the next lag report (sent at an interval with ~1 second of\n // jitter) before becoming ready.\n switchoverRestoreReadinessGracePeriodMs:\n (replicationLag.reportIntervalMs > 0\n ? replicationLag.reportIntervalMs + 1000\n : 0) + DEFAULT_READINESS_GRACE_PERIOD_MS,\n statementTimeoutMs: change.statementTimeoutMs,\n changeLogBatchSize: change.logBatchSize,\n sqliteCatchup: {\n changeLogFile: changeLogFileName(replica.file),\n readBatchRows: sqliteChangeLogReadBatchRows,\n barrierTimeoutMs: sqliteChangeLogBarrierTimeoutMs,\n },\n // The presence of these options is the writer's gate. This process\n // performs the restore and the initial sync, so the replica's\n // identity is in hand at the moment the log is opened.\n sqliteChangeLogWriter: sqliteChangeLogEnabled\n ? {\n replicaFile: replica.file,\n identity: {\n // RMv2 supplies the epoch; until then the generation and the\n // replica ID are the whole of the identity.\n epoch: null,\n generation: subscriptionState.replicaVersion,\n replicaID,\n },\n }\n : undefined,\n // The purge scheduler shares the writer's gate -- mode, not the\n // read path -- because `write` mode, with no read selector at all,\n // is the configuration it ships in.\n sqliteChangeLogPurge: sqliteChangeLogEnabled\n ? {\n retentionMs: sqliteChangeLogRetentionMs,\n batchRows: sqliteChangeLogPurgeBatchRows,\n }\n : undefined,\n // Compare mode runs both advisory checks. Postgres remains authoritative.\n sqliteChangeLogCompare:\n pgChangeLogEnabled && sqliteChangeLogComparing\n ? {\n replicaFile: replica.file,\n comparePercent: sqliteChangeLogComparePercent,\n retentionMs: sqliteChangeLogRetentionMs,\n readBatchRows: sqliteChangeLogReadBatchRows,\n }\n : undefined,\n // Slice 11 lands dark by default: serve mode constructs the stable\n // router, while readPercent=0 keeps every catchup on PG and emits\n // eligibility metrics before any canary traffic is enabled.\n sqliteChangeLogServe:\n sqliteChangeLogMode === 'serve'\n ? {\n readPercent: sqliteChangeLogReadPercent,\n coldReadPercent: sqliteChangeLogColdReadPercent,\n retentionMs: sqliteChangeLogRetentionMs,\n }\n : undefined,\n },\n setTimeout,\n );\n backupURL = destinationBackupURL;\n waitForFirstBackupBeforeServing = waitForBackupBeforeServing;\n newBackupLineage = initNewBackupLineage;\n break;\n } catch (e) {\n // Release the resources acquired by the failed attempt, e.g.:\n // * The claimed replication slot, which would otherwise remain active\n // indefinitely, preventing a retry from resuming (or forking) it.\n // * The purge lock. This is safe because the purge lock exists to\n // preserve change-log entries so the new change-streamer can resume\n // from the backup replica's watermark. An AutoResetSignal means we\n // can't resume from the backup replica (e.g. its replication slot is\n // gone), so the change-log entries the lock was protecting are no\n // longer needed. The retry performs a fresh initial sync with a new\n // replication slot, independent of the old change-log. Releasing is\n // also necessary to avoid a self-deadlock when CHANGE_DB ==\n // UPSTREAM_DB: CREATE_REPLICATION_SLOT waits for all older\n // transactions to finish, including this lock's open transaction.\n await cleanup.release();\n if (first && e instanceof AutoResetSignal) {\n lc.warn?.(`resetting replica ${replica.file}`, e);\n // TODO: Make deleteLiteDB work with litestream. It will probably have to be\n // a semantic wipe instead of a file delete.\n deleteLiteDB(replica.file);\n // The change log carries the identity of the replica it was written\n // beside, and the retry performs a fresh initial sync with a new\n // replicaVersion. Reconciliation would catch that on its own (as\n // 'identity-mismatch') but only after the writer opened a file that is\n // known here to be garbage.\n deleteChangeLogDB(replica.file);\n continue; // execute again with a fresh initial-sync\n }\n if (e instanceof DatabaseInitError) {\n throw new Error(\n `Cannot open ZERO_REPLICA_FILE at \"${replica.file}\". Please check that the path is valid.`,\n {cause: e},\n );\n }\n throw e;\n }\n }\n // impossible: upstream must have advanced in order for replication to be stuck.\n assert(changeStreamer, `resetting replica did not advance replicaVersion`);\n\n // The backup-replicator is stopped with the other services, i.e. after the\n // ChangeStreamerHttpServer has drained its subscribers.\n const processes = new SupportingProcesses(lc, parent);\n const profileSubWorkers: Worker[] = [];\n const {promise: replicatorReady, resolve} = resolver();\n if (!backupURL) {\n resolve(); // No backup replicator to wait for, so resolve immediately.\n } else {\n lc.info?.('setting up backup to', backupURL);\n config.litestream.backupURLOverride = backupURL;\n if (newBackupLineage) {\n deleteLitestreamMetaDir(replica.file);\n }\n // Start a backup replicator, which starts up the corresponding\n // litestream backup process.\n const backupReplicator = processes\n .add(\n childWorker(\n REPLICATOR_URL,\n {...env, ZERO_LITESTREAM_BACKUP_URL_OVERRIDE: backupURL},\n 'backup' satisfies ReplicaFileMode,\n ),\n 'backup-replicator',\n )\n .onceMessageType('ready', () => resolve());\n profileSubWorkers.push(backupReplicator);\n // Relay profileResponse messages from backup-replicator up to parent\n backupReplicator.onMessageType<ProfileResponseMessage>(\n 'profileResponse',\n res => parent.send(['profileResponse', res]),\n );\n }\n\n const backupMonitor = createBackupCleanupMonitor({\n lc,\n config,\n replicaFile: replica.file,\n changeStreamer,\n });\n\n let backupReady = promiseVoid;\n if (waitForFirstBackupBeforeServing) {\n const start = performance.now();\n lc.info?.(`awaiting initial backup ...`);\n\n backupReady = backupMonitor.firstBackupReceived().then(() => {\n const elapsed = performance.now() - start;\n lc.info?.(`initial backup confirmed after ${elapsed.toFixed(2)}ms`);\n });\n }\n\n // In RMv2, readiness additionally waits for the backup-replicator to catch\n // up to the replication stream. This is not done in RMv1 (i.e. when the\n // PG change-log is enabled), in which the change-streamer itself is only\n // started after the readinessGate (plus a startup delay), and thus the\n // backup-replicator cannot catch up before then.\n const readinessGate = pgChangeLogEnabled\n ? backupReady\n : Promise.all([backupReady, replicatorReady]);\n\n // Create the broadcast facade once: each broadcastWorker() adds permanent\n // 'message' forwarders to every sub worker, so creating one per /profz\n // request would leak a forwarder per request.\n const profileWorker =\n profileSubWorkers.length > 0\n ? broadcastWorker(profileSubWorkers)\n : undefined;\n const getProfileWorker = profileWorker\n ? () => Promise.resolve(profileWorker)\n : undefined;\n\n const changeStreamerWebServer = new ChangeStreamerHttpServer(\n lc,\n {\n port,\n keepaliveTimeoutMs,\n // The startup delay is only relevant when taking over the PG change-log\n // (RMv1 and RMv1.5), and is disabled for RMv2.\n startupDelayMs: pgChangeLogEnabled ? startupDelayMs : 0,\n readinessGate,\n config,\n getProfileWorker,\n },\n parent,\n changeStreamer,\n );\n\n void readinessGate.then(() => parent.send(['ready', {ready: true}]));\n\n // Note: The changeStreamer itself is not started here; it is started by the\n // changeStreamerWebServer after a delay to ensure that routing\n // routing elements have registered the server before the\n // change-streamer takes over the replication slot.\n // TODO: Remove this delay and start the changeStreamer normally here once\n // transitioned to RMv2, since changeStreamer startup will no longer\n // disrupt an existing task.\n running = true;\n await runUntilKilled(\n lc,\n parent,\n changeStreamerWebServer,\n backupMonitor,\n processes,\n );\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () =>\n runWorker(\n must(parentWorker),\n process.env,\n ...process.argv.slice(2),\n ).catch(async e => {\n if (!running) {\n await publishCriticalEvent(\n lc,\n replicationStatusError(lc, 'Initializing', e),\n );\n }\n throw e;\n }),\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"backfill-stream.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/backfill-stream.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAqBjD,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAGV,eAAe,EAGhB,MAAM,wBAAwB,CAAC;AAahC,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,mBAAmB,CAAC;AAoC/C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIvD;AAmCD,KAAK,aAAa,GAAG;IACnB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEzC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAE/B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;CAC7D,CAAC;AAYF;;;;;GAKG;AACH,wBAAuB,cAAc,CACnC,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,EAAC,IAAI,EAAE,YAAY,EAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC,EAC5D,EAAE,EAAE,eAAe,EACnB,IAAI,GAAE,aAAkB,GACvB,cAAc,CAAC,eAAe,CAAC,CAkJjC"}
1
+ {"version":3,"file":"backfill-stream.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/backfill-stream.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAsBjD,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAGV,eAAe,EAGhB,MAAM,wBAAwB,CAAC;AAahC,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,mBAAmB,CAAC;AAoC/C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIvD;AAmCD,KAAK,aAAa,GAAG;IACnB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEzC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAE/B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;CAC7D,CAAC;AAYF;;;;;GAKG;AACH,wBAAuB,cAAc,CACnC,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,EAAC,IAAI,EAAE,YAAY,EAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC,EAC5D,EAAE,EAAE,eAAe,EACnB,IAAI,GAAE,aAAkB,GACvB,cAAc,CAAC,eAAe,CAAC,CAyJjC"}
@@ -15,6 +15,7 @@ import { BinaryCopyParser, hasBinaryDecoder, makeBinaryDecoder, textCastDecoder
15
15
  import { TsvParser } from "../../../db/pg-copy.js";
16
16
  import { getTypeParsers } from "../../../db/pg-type-parser.js";
17
17
  import { TransactionPool, importSnapshot } from "../../../db/transaction-pool.js";
18
+ import { hasPostgresPatch } from "../../../types/pg-patch.js";
18
19
  import { columnMetadataSchema, tableMetadataSchema } from "./backfill-metadata.js";
19
20
  import { createReplicationSlot } from "./replication-slots.js";
20
21
  import { makeBinarySelectExprs, makeDownloadStatements } from "./initial-sync.js";
@@ -114,10 +115,11 @@ async function* streamBackfill(lc, upstreamURI, { slot, publications }, bf, opts
114
115
  throw e;
115
116
  } finally {
116
117
  tx?.setDone();
117
- if (completed) {
118
+ if (completed && hasPostgresPatch()) {
118
119
  await tx?.done();
119
120
  await db.end();
120
- } else db.end({ timeout: 5 }).catch((e) => lc.warn?.(`error closing backfill connection`, e));
121
+ } else if (completed) db.end().catch((e) => lc.warn?.(`error closing backfill connection`, e));
122
+ else db.end({ timeout: 5 }).catch((e) => lc.warn?.(`error closing backfill connection`, e));
121
123
  }
122
124
  }
123
125
  async function* stream(lc, tx, backfill, { timeline, resumable, start: startMark }, { getTotalRows, getTotalBytes }, copyCommand, parser, decoders, ctidDecoder, flushThresholdBytes) {