@nxgt/mongo-meilisearch 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +149 -4
  2. package/dist/index.d.ts +5 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +201 -90
  5. package/dist/index.js.map +10 -9
  6. package/dist/{follow.d.ts → run/follow.d.ts} +2 -2
  7. package/dist/run/follow.d.ts.map +1 -0
  8. package/dist/{lease.d.ts → run/lease.d.ts} +2 -2
  9. package/dist/run/lease.d.ts.map +1 -0
  10. package/dist/{reindex.d.ts → run/reindex.d.ts} +2 -2
  11. package/dist/run/reindex.d.ts.map +1 -0
  12. package/dist/sync/batch.d.ts.map +1 -0
  13. package/dist/{context.d.ts → sync/context.d.ts} +1 -1
  14. package/dist/sync/context.d.ts.map +1 -0
  15. package/dist/sync/create-search-sync.d.ts.map +1 -0
  16. package/dist/sync/documents.d.ts.map +1 -0
  17. package/dist/sync/errors.d.ts.map +1 -0
  18. package/dist/sync/running.d.ts.map +1 -0
  19. package/dist/sync/start.d.ts.map +1 -0
  20. package/dist/sync/state.d.ts.map +1 -0
  21. package/dist/sync/types.d.ts.map +1 -0
  22. package/dist/syncs/create-search-syncs.d.ts +13 -0
  23. package/dist/syncs/create-search-syncs.d.ts.map +1 -0
  24. package/dist/syncs/types.d.ts +89 -0
  25. package/dist/syncs/types.d.ts.map +1 -0
  26. package/docs/README.md +3 -1
  27. package/docs/guide/search-syncs/lifecycle.md +301 -0
  28. package/docs/guide/search-syncs/wiring.md +188 -0
  29. package/docs/roadmap.md +18 -0
  30. package/docs/troubleshooting.md +213 -1
  31. package/package.json +4 -4
  32. package/dist/batch.d.ts.map +0 -1
  33. package/dist/context.d.ts.map +0 -1
  34. package/dist/create-search-sync.d.ts.map +0 -1
  35. package/dist/documents.d.ts.map +0 -1
  36. package/dist/errors.d.ts.map +0 -1
  37. package/dist/follow.d.ts.map +0 -1
  38. package/dist/lease.d.ts.map +0 -1
  39. package/dist/reindex.d.ts.map +0 -1
  40. package/dist/running.d.ts.map +0 -1
  41. package/dist/start.d.ts.map +0 -1
  42. package/dist/state.d.ts.map +0 -1
  43. package/dist/types.d.ts.map +0 -1
  44. /package/dist/{batch.d.ts → sync/batch.d.ts} +0 -0
  45. /package/dist/{create-search-sync.d.ts → sync/create-search-sync.d.ts} +0 -0
  46. /package/dist/{documents.d.ts → sync/documents.d.ts} +0 -0
  47. /package/dist/{errors.d.ts → sync/errors.d.ts} +0 -0
  48. /package/dist/{running.d.ts → sync/running.d.ts} +0 -0
  49. /package/dist/{start.d.ts → sync/start.d.ts} +0 -0
  50. /package/dist/{state.d.ts → sync/state.d.ts} +0 -0
  51. /package/dist/{types.d.ts → sync/types.d.ts} +0 -0
package/dist/index.js.map CHANGED
@@ -1,20 +1,21 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/context.ts", "../src/reindex.ts", "../src/batch.ts", "../src/errors.ts", "../src/documents.ts", "../src/lease.ts", "../src/running.ts", "../src/state.ts", "../src/follow.ts", "../src/start.ts", "../src/create-search-sync.ts"],
3
+ "sources": ["../src/run/reindex.ts", "../src/sync/batch.ts", "../src/sync/errors.ts", "../src/sync/documents.ts", "../src/sync/running.ts", "../src/sync/state.ts", "../src/run/lease.ts", "../src/sync/context.ts", "../src/run/follow.ts", "../src/sync/start.ts", "../src/sync/create-search-sync.ts", "../src/syncs/create-search-syncs.ts"],
4
4
  "sourcesContent": [
5
- "import type { AnyIndexDefinition, TypedIndex } from '@nxgt/meilisearch';\nimport type { AnyCollectionDefinition, TypedCollection } from '@nxgt/mongo';\nimport type { Collection } from 'mongodb';\nimport type { LeaseDocument } from './lease';\nimport type { SearchSyncOptions, SearchSyncState } from './types';\n\n/** Loosely typed: this layer works on any documents; the public types are what callers see. */\nexport type Doc = Record<string, unknown>;\n\n/**\n * What a sync works from, resolved once. Data, and the caller's own two\n * functions as they were given.\n */\nexport interface SyncContext {\n\treadonly name: string;\n\treadonly collection: TypedCollection<AnyCollectionDefinition>;\n\treadonly index: TypedIndex<AnyIndexDefinition>;\n\treadonly primaryKey: string;\n\treadonly state: Collection<SearchSyncState>;\n\t/** The same collection, for the lease documents it also holds. */\n\treadonly leases: Collection<LeaseDocument>;\n\t/** How long a lease lasts unrenewed, in ms. */\n\treadonly leaseMs: number;\n\treadonly batchSize: number;\n\treadonly flushIntervalMs: number;\n\treadonly positionIntervalMs: number;\n\treadonly pageSize: number;\n\treadonly onHistoryLost: 'reindex' | 'fail';\n\treadonly transform: (document: Doc) => unknown;\n\treadonly toIndexId: (id: unknown) => unknown;\n\t/**\n\t * Whether this sync is following changes in this process. A reindex\n\t * beside its own follower would remove what the follower has just\n\t * indexed, and the follower would never send it again.\n\t */\n\treadonly follower: { running: boolean };\n}\n\nconst DEFAULT_STATE_COLLECTION = 'nxgt_search_sync';\n\nfunction positive(option: string, value: number | undefined, fallback: number) {\n\tconst resolved = value ?? fallback;\n\tif (!Number.isInteger(resolved) || resolved < 1) {\n\t\tthrow new TypeError(\n\t\t\t`createSearchSync: ${option} must be a whole number above 0, not ${String(value)}`,\n\t\t);\n\t}\n\treturn resolved;\n}\n\nexport function createContext(\n\toptions: SearchSyncOptions<AnyCollectionDefinition, AnyIndexDefinition>,\n): SyncContext {\n\tconst { collection, index } = options;\n\tif (typeof options.transform !== 'function') {\n\t\tthrow new TypeError('createSearchSync: transform must be a function');\n\t}\n\tconst name = options.name ?? `${collection.collectionName}:${index.uid}`;\n\tif (name === '') {\n\t\tthrow new TypeError('createSearchSync: name must not be empty');\n\t}\n\tconst flushIntervalMs = options.flushIntervalMs ?? 1000;\n\tif (!Number.isInteger(flushIntervalMs) || flushIntervalMs < 0) {\n\t\tthrow new TypeError(\n\t\t\t`createSearchSync: flushIntervalMs must be a whole number of milliseconds, not ${String(options.flushIntervalMs)}`,\n\t\t);\n\t}\n\tconst stateCollection = options.stateCollection ?? DEFAULT_STATE_COLLECTION;\n\treturn {\n\t\tname,\n\t\tcollection,\n\t\tindex,\n\t\tprimaryKey: index.definition.primaryKey as string,\n\t\tstate: collection.db.collection<SearchSyncState>(stateCollection),\n\t\tleases: collection.db.collection<LeaseDocument>(stateCollection),\n\t\tleaseMs: positive('leaseMs', options.leaseMs, 30_000),\n\t\tbatchSize: positive('batchSize', options.batchSize, 500),\n\t\tpositionIntervalMs: positive(\n\t\t\t'positionIntervalMs',\n\t\t\toptions.positionIntervalMs,\n\t\t\t60_000,\n\t\t),\n\t\tflushIntervalMs,\n\t\tpageSize: positive('pageSize', options.pageSize, 100),\n\t\tonHistoryLost: options.onHistoryLost ?? 'reindex',\n\t\ttransform: options.transform as (document: Doc) => unknown,\n\t\ttoIndexId: (options.toIndexId ?? String) as (id: unknown) => unknown,\n\t\tfollower: { running: false },\n\t};\n}\n",
6
- "import type { ResumeToken } from '@nxgt/mongo';\nimport { MeilisearchApiError } from 'meilisearch';\nimport { deleteIds, sendDocuments } from './batch';\nimport type { Doc, SyncContext } from './context';\nimport { entryOf, keyOf } from './documents';\nimport { failed } from './errors';\nimport { confirmLease, type HeldLease, leaseLost, withLease } from './lease';\nimport { checkIdle } from './running';\nimport { saveState } from './state';\nimport type { ReindexReport } from './types';\n\n/**\n * Where the collection's changes are now, without waiting for one: a new\n * stream's first read answers with its position. The server holds that read\n * for up to `maxAwaitTimeMS` — a second by default, measured.\n */\nasync function currentToken(ctx: SyncContext): Promise<ResumeToken> {\n\tconst stream = ctx.collection.raw.watch([], { maxAwaitTimeMS: 1 });\n\ttry {\n\t\tawait stream.tryNext();\n\t\treturn stream.resumeToken as unknown as ResumeToken;\n\t} finally {\n\t\tawait stream.close();\n\t}\n}\n\n/** Every live document through the transform, a page at a time. */\nasync function sendAll(\n\tctx: SyncContext,\n\twanted: Set<string>,\n\tlease: HeldLease,\n) {\n\tlet indexed = 0;\n\tlet skipped = 0;\n\tlet after: string | null | undefined;\n\tdo {\n\t\tconst page = await ctx.collection.paginateByCursor({\n\t\t\tafter,\n\t\t\tlimit: ctx.pageSize,\n\t\t});\n\t\tconst documents: Doc[] = [];\n\t\tfor (const document of page.items as Doc[]) {\n\t\t\tconst entry = await entryOf(ctx, document['_id'], document);\n\t\t\tif (entry.kind === 'delete') {\n\t\t\t\tskipped += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twanted.add(entry.key);\n\t\t\tdocuments.push(entry.document);\n\t\t}\n\t\tawait sendDocuments(ctx, documents);\n\t\tindexed += documents.length;\n\t\t// Found lost by a renewal: stop sending beside whoever holds it now.\n\t\tif (lease.lost) throw leaseLost(ctx);\n\t\tafter = page.nextCursor;\n\t} while (after);\n\treturn { indexed, skipped };\n}\n\nconst LIST_LIMIT = 1000;\n\n/** Nothing was ever written to it: an index that does not exist holds nothing. */\nconst missingIndex = (error: unknown) =>\n\terror instanceof MeilisearchApiError &&\n\terror.cause?.code === 'index_not_found';\n\n/** Takes out what the index holds and the collection no longer gives it. */\nasync function removeUnwanted(\n\tctx: SyncContext,\n\twanted: Set<string>,\n): Promise<number> {\n\tconst unwanted: unknown[] = [];\n\tfor (let offset = 0; ; offset += LIST_LIMIT) {\n\t\tconst page = await ctx.index.raw\n\t\t\t.getDocuments({ fields: [ctx.primaryKey], limit: LIST_LIMIT, offset })\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (missingIndex(error)) return undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\tif (!page) return 0;\n\t\tfor (const document of page.results as Doc[]) {\n\t\t\tconst id = document[ctx.primaryKey];\n\t\t\tif (!wanted.has(keyOf(id))) unwanted.push(id);\n\t\t}\n\t\tif (offset + LIST_LIMIT >= page.total) break;\n\t}\n\tawait deleteIds(ctx, unwanted);\n\treturn unwanted.length;\n}\n\n/**\n * A reindex, holding the sync's lease for as long as it runs: a follower in\n * another process would otherwise have what it just indexed removed, and\n * never send it again.\n */\nexport async function reindex(ctx: SyncContext): Promise<ReindexReport> {\n\tcheckIdle(ctx, 'reindex');\n\treturn withLease(ctx, 'reindex', (lease) => reindexHeld(ctx, lease));\n}\n\n/**\n * A reindex by a caller that holds the lease. What removes documents and what\n * records a point each go only once the server confirms the lease is still\n * this one's: after another process took it over, either would undo what\n * that process just did.\n */\nexport async function reindexHeld(\n\tctx: SyncContext,\n\tlease: HeldLease,\n): Promise<ReindexReport> {\n\ttry {\n\t\t// Taken first: a change made while the documents are read is followed\n\t\t// again from here, so it cannot fall between the two.\n\t\tconst token = await currentToken(ctx);\n\t\tconst wanted = new Set<string>();\n\t\tconst { indexed, skipped } = await sendAll(ctx, wanted, lease);\n\t\tawait confirmLease(ctx, lease);\n\t\tconst removed = await removeUnwanted(ctx, wanted);\n\t\tawait confirmLease(ctx, lease);\n\t\tawait saveState(ctx, token, true);\n\t\treturn { indexed, skipped, removed };\n\t} catch (error) {\n\t\tthrow failed(ctx.name, 'reindexing', error);\n\t}\n}\n",
5
+ "import type { ResumeToken } from '@nxgt/mongo';\nimport { MeilisearchApiError } from 'meilisearch';\nimport { deleteIds, sendDocuments } from '../sync/batch';\nimport type { Doc, SyncContext } from '../sync/context';\nimport { entryOf, keyOf } from '../sync/documents';\nimport { failed } from '../sync/errors';\nimport { checkIdle } from '../sync/running';\nimport { saveState } from '../sync/state';\nimport type { ReindexReport } from '../sync/types';\nimport { confirmLease, type HeldLease, leaseLost, withLease } from './lease';\n\n/**\n * Where the collection's changes are now, without waiting for one: a new\n * stream's first read answers with its position. The server holds that read\n * for up to `maxAwaitTimeMS` — a second by default, measured.\n */\nasync function currentToken(ctx: SyncContext): Promise<ResumeToken> {\n\tconst stream = ctx.collection.raw.watch([], { maxAwaitTimeMS: 1 });\n\ttry {\n\t\tawait stream.tryNext();\n\t\treturn stream.resumeToken as unknown as ResumeToken;\n\t} finally {\n\t\tawait stream.close();\n\t}\n}\n\n/** Every live document through the transform, a page at a time. */\nasync function sendAll(\n\tctx: SyncContext,\n\twanted: Set<string>,\n\tlease: HeldLease,\n) {\n\tlet indexed = 0;\n\tlet skipped = 0;\n\tlet after: string | null | undefined;\n\tdo {\n\t\tconst page = await ctx.collection.paginateByCursor({\n\t\t\tafter,\n\t\t\tlimit: ctx.pageSize,\n\t\t});\n\t\tconst documents: Doc[] = [];\n\t\tfor (const document of page.items as Doc[]) {\n\t\t\tconst entry = await entryOf(ctx, document['_id'], document);\n\t\t\tif (entry.kind === 'delete') {\n\t\t\t\tskipped += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twanted.add(entry.key);\n\t\t\tdocuments.push(entry.document);\n\t\t}\n\t\tawait sendDocuments(ctx, documents);\n\t\tindexed += documents.length;\n\t\t// Found lost by a renewal: stop sending beside whoever holds it now.\n\t\tif (lease.lost) throw leaseLost(ctx);\n\t\tafter = page.nextCursor;\n\t} while (after);\n\treturn { indexed, skipped };\n}\n\nconst LIST_LIMIT = 1000;\n\n/** Nothing was ever written to it: an index that does not exist holds nothing. */\nconst missingIndex = (error: unknown) =>\n\terror instanceof MeilisearchApiError &&\n\terror.cause?.code === 'index_not_found';\n\n/** Takes out what the index holds and the collection no longer gives it. */\nasync function removeUnwanted(\n\tctx: SyncContext,\n\twanted: Set<string>,\n): Promise<number> {\n\tconst unwanted: unknown[] = [];\n\tfor (let offset = 0; ; offset += LIST_LIMIT) {\n\t\tconst page = await ctx.index.raw\n\t\t\t.getDocuments({ fields: [ctx.primaryKey], limit: LIST_LIMIT, offset })\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (missingIndex(error)) return undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\tif (!page) return 0;\n\t\tfor (const document of page.results as Doc[]) {\n\t\t\tconst id = document[ctx.primaryKey];\n\t\t\tif (!wanted.has(keyOf(id))) unwanted.push(id);\n\t\t}\n\t\tif (offset + LIST_LIMIT >= page.total) break;\n\t}\n\tawait deleteIds(ctx, unwanted);\n\treturn unwanted.length;\n}\n\n/**\n * A reindex, holding the sync's lease for as long as it runs: a follower in\n * another process would otherwise have what it just indexed removed, and\n * never send it again.\n */\nexport async function reindex(ctx: SyncContext): Promise<ReindexReport> {\n\tcheckIdle(ctx, 'reindex');\n\treturn withLease(ctx, 'reindex', (lease) => reindexHeld(ctx, lease));\n}\n\n/**\n * A reindex by a caller that holds the lease. What removes documents and what\n * records a point each go only once the server confirms the lease is still\n * this one's: after another process took it over, either would undo what\n * that process just did.\n */\nexport async function reindexHeld(\n\tctx: SyncContext,\n\tlease: HeldLease,\n): Promise<ReindexReport> {\n\ttry {\n\t\t// Taken first: a change made while the documents are read is followed\n\t\t// again from here, so it cannot fall between the two.\n\t\tconst token = await currentToken(ctx);\n\t\tconst wanted = new Set<string>();\n\t\tconst { indexed, skipped } = await sendAll(ctx, wanted, lease);\n\t\tawait confirmLease(ctx, lease);\n\t\tconst removed = await removeUnwanted(ctx, wanted);\n\t\tawait confirmLease(ctx, lease);\n\t\tawait saveState(ctx, token, true);\n\t\treturn { indexed, skipped, removed };\n\t} catch (error) {\n\t\tthrow failed(ctx.name, 'reindexing', error);\n\t}\n}\n",
7
6
  "import type { Doc, SyncContext } from './context';\nimport type { Entry } from './documents';\n\n/**\n * How long a write may take to be applied. Meilisearch's own default is\n * five seconds, which a batch on a large index outlasts.\n */\nconst WAIT = { wait: { timeout: 120_000 } } as const;\n\nfunction chunks<T>(items: readonly T[], size: number): T[][] {\n\tconst out: T[][] = [];\n\tfor (let i = 0; i < items.length; i += size) {\n\t\tout.push(items.slice(i, i + size));\n\t}\n\treturn out;\n}\n\n/** Adds documents, `batchSize` at a time, each batch applied before the next. */\nexport async function sendDocuments(\n\tctx: SyncContext,\n\tdocuments: readonly Doc[],\n): Promise<void> {\n\tfor (const batch of chunks(documents, ctx.batchSize)) {\n\t\tawait ctx.index.add(batch, WAIT);\n\t}\n}\n\n/** Deletes documents by id, `batchSize` at a time. */\nexport async function deleteIds(\n\tctx: SyncContext,\n\tids: readonly unknown[],\n): Promise<void> {\n\tfor (const batch of chunks(ids, ctx.batchSize)) {\n\t\tawait ctx.index.delete(batch, WAIT);\n\t}\n}\n\n/**\n * Applies entries: one per id, so adds and deletes touch different\n * documents and their order does not matter.\n */\nexport async function send(\n\tctx: SyncContext,\n\tentries: readonly Entry[],\n): Promise<void> {\n\tconst documents: Doc[] = [];\n\tconst ids: unknown[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.kind === 'upsert') documents.push(entry.document);\n\t\telse ids.push(entry.id);\n\t}\n\tawait sendDocuments(ctx, documents);\n\tawait deleteIds(ctx, ids);\n}\n",
8
7
  "/** What stopped a sync, as a string to switch on. */\nexport type SearchSyncErrorCode =\n\t/** The resume point is older than the server's change history. */\n\t| 'HISTORY_LOST'\n\t/** The transform gave a document whose primary key is not its index id. */\n\t| 'ID_MISMATCH'\n\t/** The transform gave something that is neither a document nor `null`. */\n\t| 'NOT_A_DOCUMENT'\n\t/**\n\t * This sync's name is taken: it is following changes in this process, or\n\t * another process holds its lease.\n\t */\n\t| 'RUNNING'\n\t/**\n\t * A running sync's lease was not renewed in time and is no longer its\n\t * own: it stopped rather than follow beside whoever took the name over.\n\t */\n\t| 'LEASE_LOST'\n\t/** Anything else: the cause says what. */\n\t| 'FAILED';\n\nexport interface SearchSyncErrorOptions {\n\tcode: SearchSyncErrorCode;\n\tsync: string;\n\tcause?: unknown;\n\t/** Who holds the lease, on a `RUNNING` the lease refused. */\n\tholder?: string | undefined;\n\t/** When that lease lapses unrenewed, on a `RUNNING` the lease refused. */\n\texpiresAt?: Date | undefined;\n}\n\n/** An error of a sync, with the sync's name and what caused it. */\nexport class SearchSyncError extends Error {\n\toverride name = 'SearchSyncError';\n\treadonly code: SearchSyncErrorCode;\n\t/** The sync's name. */\n\treadonly sync: string;\n\t/**\n\t * On a `RUNNING` the lease refused: who holds the name, as that process\n\t * described itself (`host:pid:<ObjectId>`). A label for a log, not a value\n\t * to parse. `undefined` on every other error, on the `RUNNING` of a sync\n\t * object that is already following in this process, and when the holder\n\t * let go before it could be read.\n\t */\n\treadonly holder: string | undefined;\n\t/**\n\t * On a `RUNNING` the lease refused: when the holder's lease lapses if it\n\t * stops renewing it, read from the lease document and so on the MongoDB\n\t * server's clock. A live holder renews it every third of `leaseMs`, so a\n\t * start at this time can still be refused, with a later one. `undefined`\n\t * whenever `holder` is.\n\t */\n\treadonly expiresAt: Date | undefined;\n\n\tconstructor(message: string, options: SearchSyncErrorOptions) {\n\t\tsuper(\n\t\t\tmessage,\n\t\t\toptions.cause === undefined ? undefined : { cause: options.cause },\n\t\t);\n\t\tthis.code = options.code;\n\t\tthis.sync = options.sync;\n\t\tthis.holder = options.holder;\n\t\tthis.expiresAt = options.expiresAt;\n\t}\n}\n\n/** `error` as a `SearchSyncError`, kept as it is when it already is one. */\nexport function failed(sync: string, doing: string, error: unknown) {\n\tif (error instanceof SearchSyncError) return error;\n\tconst reason = error instanceof Error ? error.message : String(error);\n\treturn new SearchSyncError(\n\t\t`Search sync \"${sync}\" failed ${doing}: ${reason}`,\n\t\t{\n\t\t\tcode: 'FAILED',\n\t\t\tsync,\n\t\t\tcause: error,\n\t\t},\n\t);\n}\n",
9
8
  "import type { Doc, SyncContext } from './context';\nimport { SearchSyncError } from './errors';\n\n/** One document's fate in the index: sent, or taken out. */\nexport type Entry =\n\t| { kind: 'upsert'; key: string; document: Doc }\n\t| { kind: 'delete'; key: string; id: unknown };\n\n/** An id as a key: `1` and `'1'` are different ids to Meilisearch. */\nexport const keyOf = (id: unknown) => `${typeof id}:${String(id)}`;\n\nconst isRecord = (value: unknown): value is Doc =>\n\ttypeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** What a transform gave back, for a message: its shape, never its value. */\nfunction describe(value: unknown): string {\n\tif (Array.isArray(value)) return 'an array';\n\tif (value === undefined) return 'undefined';\n\treturn `a ${typeof value}`;\n}\n\n/**\n * What the index should hold for a document of the collection: what the\n * transform makes of it, or nothing when it is gone or kept out.\n */\nexport async function entryOf(\n\tctx: SyncContext,\n\tmongoId: unknown,\n\tdocument: Doc | undefined,\n): Promise<Entry> {\n\tconst id = ctx.toIndexId(mongoId);\n\tconst key = keyOf(id);\n\tif (!document) return { kind: 'delete', key, id };\n\tconst indexed = await ctx.transform(document);\n\tif (indexed === null) return { kind: 'delete', key, id };\n\tif (!isRecord(indexed)) {\n\t\t// A `SearchSyncError` with a code of its own, like the id mismatch\n\t\t// below it: a bare `TypeError` came back through `failed()` as\n\t\t// `FAILED`, the code that means \"anything else\", and named neither the\n\t\t// sync nor the document whose transform did it.\n\t\tthrow new SearchSyncError(\n\t\t\t`Search sync \"${ctx.name}\": transform gave ${describe(indexed)} for ` +\n\t\t\t\t`the document ${String(mongoId)}. It must give a document to ` +\n\t\t\t\t'index, or null to keep it out.',\n\t\t\t{ code: 'NOT_A_DOCUMENT', sync: ctx.name },\n\t\t);\n\t}\n\tconst given = indexed[ctx.primaryKey];\n\tif (keyOf(given) !== key) {\n\t\tthrow new SearchSyncError(\n\t\t\t`Search sync \"${ctx.name}\": transform gave \"${ctx.primaryKey}\" ` +\n\t\t\t\t`${JSON.stringify(given)} for the document ${String(mongoId)}, whose ` +\n\t\t\t\t`index id is ${JSON.stringify(id)}. A document under another id ` +\n\t\t\t\t'could never be taken out of the index again.',\n\t\t\t{ code: 'ID_MISMATCH', sync: ctx.name },\n\t\t);\n\t}\n\treturn { kind: 'upsert', key, document: indexed };\n}\n",
10
- "import { hostname } from 'node:os';\nimport { ObjectId } from 'mongodb';\nimport type { SyncContext } from './context';\nimport { failed, SearchSyncError } from './errors';\n\n/**\n * The lease on a sync's name: one document in the state collection, whose\n * `_id` is `{ lease: <name> }` — an object, so it can never be a sync's own\n * state, whose `_id` is the name itself.\n *\n * Taking it is one atomic write whose update decides — the server, not a\n * read before a write. Every time is the server's (`$$NOW`), so hosts whose\n * clocks disagree still agree on when a lease lapses.\n *\n * Renewal, release and the holder's name follow `@nxgt/mongo`'s migration\n * lock (`migrations/lock.ts`); taking it does not — see `take`.\n */\nexport interface LeaseDocument {\n\t_id: { lease: string };\n\tholder: string;\n\tacquiredAt: Date;\n\texpiresAt: Date;\n}\n\n/** A held lease: data, and whether a renewal found it gone. */\nexport interface HeldLease {\n\treadonly holder: string;\n\tlost: boolean;\n}\n\nconst idOf = (ctx: SyncContext) => ({ lease: ctx.name });\n\n/**\n * Inserts the lease, or takes over one that lapsed, in one atomic upsert.\n *\n * The filter is `_id` alone — the server allows no `$expr` in an upsert's\n * filter — and the decision is made by the update instead: a pipeline that\n * keeps the current holder's fields while its lease runs, and writes this\n * holder's when there is none or it lapsed. What comes back says who won.\n * It is a `findAndModify`, not an `aggregate`: `@nxgt/mongo`'s migration lock\n * takes its lock with `$documents`/`$merge`, which a failpoint or a monitor\n * aimed at the change stream's `aggregate` would also catch.\n */\nasync function take(ctx: SyncContext, holder: string): Promise<boolean> {\n\tconst free = {\n\t\t$or: [\n\t\t\t{ $eq: [{ $type: '$expiresAt' }, 'missing'] },\n\t\t\t{ $lte: ['$expiresAt', '$$NOW'] },\n\t\t],\n\t};\n\tconst pick = (mine: unknown, theirs: string) => ({\n\t\t$cond: [free, mine, theirs],\n\t});\n\ttry {\n\t\tconst after = await ctx.leases.findOneAndUpdate(\n\t\t\t{ _id: idOf(ctx) },\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\t$set: {\n\t\t\t\t\t\tholder: pick({ $literal: holder }, '$holder'),\n\t\t\t\t\t\tacquiredAt: pick('$$NOW', '$acquiredAt'),\n\t\t\t\t\t\texpiresAt: pick({ $add: ['$$NOW', ctx.leaseMs] }, '$expiresAt'),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t\t{ upsert: true, returnDocument: 'after' },\n\t\t);\n\t\treturn after?.holder === holder;\n\t} catch (error) {\n\t\t// Two upserts racing to insert the same `_id`: the other one won.\n\t\tif ((error as { code?: number }).code === 11000) return false;\n\t\tthrow error;\n\t}\n}\n\n/**\n * Takes the lease, or throws `RUNNING` naming who holds it and until when —\n * in the message and as `holder` and `expiresAt`, so a standby can wait for\n * that moment rather than a fixed time. The same code a second `start` in\n * this process gets, since it means the same thing: this name is being\n * followed.\n */\nexport async function acquire(\n\tctx: SyncContext,\n\tdoing: string,\n): Promise<HeldLease> {\n\tconst holder = `${hostname()}:${process.pid}:${new ObjectId().toHexString()}`;\n\tlet current: LeaseDocument | null = null;\n\ttry {\n\t\t// Twice at most: a holder that let go between the take and the read\n\t\t// left the name free, and refusing it then would be wrong.\n\t\tfor (let attempt = 0; attempt < 2; attempt += 1) {\n\t\t\tif (await take(ctx, holder)) return { holder, lost: false };\n\t\t\tcurrent = await ctx.leases.findOne({ _id: idOf(ctx) });\n\t\t\tif (current) break;\n\t\t}\n\t} catch (error) {\n\t\tthrow failed(ctx.name, 'taking its lease', error);\n\t}\n\t// Each only when the document holds what it should; `expiresAt` only\n\t// beside a holder, as the error's own documentation promises.\n\tconst until =\n\t\tcurrent?.expiresAt instanceof Date ? current.expiresAt : undefined;\n\tconst theirs =\n\t\ttypeof current?.holder === 'string' ? current.holder : undefined;\n\tconst expiresAt = theirs === undefined ? undefined : until;\n\tthrow new SearchSyncError(\n\t\t`Search sync \"${ctx.name}\" is held by ${theirs ?? 'another process'} ` +\n\t\t\t`until ${until?.toISOString() ?? 'it lets go'}: ` +\n\t\t\t`wait for it to close, or for its lease to lapse, before you ${doing}.`,\n\t\t{\n\t\t\tcode: 'RUNNING',\n\t\t\tsync: ctx.name,\n\t\t\tholder: theirs,\n\t\t\texpiresAt,\n\t\t},\n\t);\n}\n\n/**\n * Pushes the lease's end back by `leaseMs`. A renewal that finds another\n * holder marks the lease lost; one that does not reach the server is tried\n * again at the next beat, and the lease lapses only if none reaches it for a\n * whole `leaseMs`.\n */\nexport async function renew(ctx: SyncContext, lease: HeldLease): Promise<void> {\n\ttry {\n\t\tconst result = await ctx.leases.updateOne(\n\t\t\t{ _id: idOf(ctx), holder: lease.holder },\n\t\t\t[{ $set: { expiresAt: { $add: ['$$NOW', ctx.leaseMs] } } }],\n\t\t);\n\t\tif (result.matchedCount === 0) lease.lost = true;\n\t} catch {\n\t\t// Tried again at the next beat.\n\t}\n}\n\n/**\n * Renews the lease now, and throws `LEASE_LOST` if it is someone else's: the\n * check before a step that must not run beside another holder.\n */\nexport async function confirmLease(\n\tctx: SyncContext,\n\tlease: HeldLease,\n): Promise<void> {\n\tlet result: { matchedCount: number };\n\ttry {\n\t\tresult = await ctx.leases.updateOne(\n\t\t\t{ _id: idOf(ctx), holder: lease.holder },\n\t\t\t[{ $set: { expiresAt: { $add: ['$$NOW', ctx.leaseMs] } } }],\n\t\t);\n\t} catch (error) {\n\t\t// Unlike a timed renewal, not tried again later: what comes next must\n\t\t// not run on a lease nobody could confirm.\n\t\tthrow failed(ctx.name, 'checking its lease', error);\n\t}\n\tif (result.matchedCount === 0) lease.lost = true;\n\tif (lease.lost) throw leaseLost(ctx);\n}\n\n/** Lets go of it, if it is still this holder's. One that fails lapses. */\nexport async function release(\n\tctx: SyncContext,\n\tlease: HeldLease,\n): Promise<void> {\n\tawait ctx.leases\n\t\t.deleteOne({ _id: idOf(ctx), holder: lease.holder })\n\t\t.catch(() => undefined);\n}\n\n/** The error a sync stops with once its lease is someone else's. */\nexport function leaseLost(ctx: SyncContext): SearchSyncError {\n\treturn new SearchSyncError(\n\t\t`Search sync \"${ctx.name}\" lost its lease: another process holds the ` +\n\t\t\t'name now, or the lease was removed (it lapses when not renewed ' +\n\t\t\t`within ${ctx.leaseMs} ms). It stopped rather than run beside it.`,\n\t\t{ code: 'LEASE_LOST', sync: ctx.name },\n\t);\n}\n\n/**\n * Renews the lease every third of `leaseMs`, and calls `onLost` once when a\n * renewal finds it someone else's. Gives back what stops the renewals.\n */\nexport function keepLease(\n\tctx: SyncContext,\n\tlease: HeldLease,\n\tonLost: () => void,\n): () => void {\n\tlet stopped = false;\n\tconst beat = setInterval(\n\t\t() => {\n\t\t\tvoid renew(ctx, lease).then(() => {\n\t\t\t\t// A renewal still in flight when it was stopped reports nothing.\n\t\t\t\tif (lease.lost && !stopped) {\n\t\t\t\t\tclearInterval(beat);\n\t\t\t\t\tonLost();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tMath.max(1, Math.floor(ctx.leaseMs / 3)),\n\t);\n\treturn () => {\n\t\tstopped = true;\n\t\tclearInterval(beat);\n\t};\n}\n\n/**\n * Runs `fn` holding the lease, renewed every third of `leaseMs`. `fn` checks\n * it is still held where that matters (`confirmLease`).\n */\nexport async function withLease<T>(\n\tctx: SyncContext,\n\tdoing: string,\n\tfn: (lease: HeldLease) => Promise<T>,\n): Promise<T> {\n\tconst lease = await acquire(ctx, doing);\n\tconst stop = keepLease(ctx, lease, () => undefined);\n\ttry {\n\t\treturn await fn(lease);\n\t} finally {\n\t\tstop();\n\t\tawait release(ctx, lease);\n\t}\n}\n",
11
9
  "import type { SyncContext } from './context';\nimport { SearchSyncError } from './errors';\n\n/**\n * Refuses to do beside a running follower what would undo its work: a\n * reindex removes what the follower has just indexed, and the follower,\n * having handled that change, never sends it again.\n *\n * Its `RUNNING` carries no `holder` and no `expiresAt`: the lease is this\n * process's own and renewed for as long as it follows, so there is no time to\n * wait for — only a `close()` frees the name.\n */\nexport function checkIdle(ctx: SyncContext, doing: string): void {\n\tif (!ctx.follower.running) return;\n\tthrow new SearchSyncError(\n\t\t`Search sync \"${ctx.name}\" is already following changes in this ` +\n\t\t\t`process: close it before you ${doing}.`,\n\t\t{ code: 'RUNNING', sync: ctx.name },\n\t);\n}\n",
12
10
  "import type { ResumeToken } from '@nxgt/mongo';\nimport type { SyncContext } from './context';\nimport { failed } from './errors';\nimport type { SearchSyncState } from './types';\n\n/** What the sync has recorded, or `undefined` before its first reindex. */\nexport async function readState(\n\tctx: SyncContext,\n): Promise<SearchSyncState | undefined> {\n\ttry {\n\t\treturn (await ctx.state.findOne({ _id: ctx.name })) ?? undefined;\n\t} catch (error) {\n\t\tthrow failed(ctx.name, 'reading its resume point', error);\n\t}\n}\n\n/** Forgets everything recorded: the next `start` reindexes. */\nexport async function clearState(ctx: SyncContext): Promise<void> {\n\ttry {\n\t\tawait ctx.state.deleteOne({ _id: ctx.name });\n\t} catch (error) {\n\t\tthrow failed(ctx.name, 'forgetting its resume point', error);\n\t}\n}\n\n/** Moves the resume point, and says a reindex finished when one did. */\nexport async function saveState(\n\tctx: SyncContext,\n\tresumeToken: ResumeToken,\n\treindexed: boolean,\n): Promise<void> {\n\tconst now = new Date();\n\ttry {\n\t\tawait ctx.state.updateOne(\n\t\t\t{ _id: ctx.name },\n\t\t\t{\n\t\t\t\t$set: {\n\t\t\t\t\tresumeToken,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t...(reindexed ? { reindexedAt: now } : {}),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ upsert: true },\n\t\t);\n\t} catch (error) {\n\t\tthrow failed(ctx.name, 'recording its resume point', error);\n\t}\n}\n",
13
- "import type {\n\tAnyCollectionDefinition,\n\tChangeOf,\n\tChangeSubscription,\n\tCloseReason,\n\tResumeToken,\n} from '@nxgt/mongo';\nimport { send } from './batch';\nimport type { Doc, SyncContext } from './context';\nimport { type Entry, entryOf } from './documents';\nimport { failed, type SearchSyncError } from './errors';\nimport { type HeldLease, keepLease, leaseLost, release } from './lease';\nimport { clearState, saveState } from './state';\nimport type { RunningSearchSync } from './types';\n\nconst noop = () => undefined;\n\n/** What a running sync holds between changes. */\ninterface Following {\n\treadonly pending: Map<string, Entry>;\n\t/** The token of the last change in `pending`. */\n\tlast: ResumeToken | undefined;\n\tsaved: ResumeToken;\n\t/** Changes whose handler has not queued them yet. */\n\tinFlight: number;\n\ttimer: ReturnType<typeof setTimeout> | undefined;\n\t/** Records where the stream is while nothing is being sent. */\n\tbeat: ReturnType<typeof setInterval> | undefined;\n\t/** Stops renewing the lease on the sync's name. */\n\tstopLease: () => void;\n\t/**\n\t * The stream was heard. Only then does `closed` let go of the lease: a\n\t * stream that never got ready leaves it to `start`, which may reindex and\n\t * open again on it after a lost history.\n\t */\n\theard: boolean;\n\t/** Flushes run one after the other. */\n\tchain: Promise<void>;\n\t/** The stream has stopped: what is queued no longer records anything. */\n\tdone: boolean;\n\tfailure: SearchSyncError | undefined;\n}\n\n/**\n * Sends what is pending, then records the token that covers it — or, with\n * nothing to send and no change being handled, where the stream is: a quiet\n * collection would otherwise keep a resume point until the server's history\n * no longer reaches it.\n */\nfunction flush(\n\tctx: SyncContext,\n\tf: Following,\n\tsubscription: ChangeSubscription,\n): Promise<void> {\n\tf.chain = f.chain.then(async () => {\n\t\tif (f.failure) throw f.failure;\n\t\tif (f.done) return;\n\t\tclearTimeout(f.timer);\n\t\tf.timer = undefined;\n\t\tconst entries = [...f.pending.values()];\n\t\tconst upTo = f.last;\n\t\tf.pending.clear();\n\t\ttry {\n\t\t\tif (entries.length > 0) await send(ctx, entries);\n\t\t\tconst idle = entries.length === 0 && f.inFlight === 0;\n\t\t\tconst at = upTo ?? (idle ? subscription.position : undefined);\n\t\t\tif (at && at !== f.saved) {\n\t\t\t\tawait saveState(ctx, at, false);\n\t\t\t\tf.saved = at;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tf.failure ??= failed(ctx.name, 'sending changes', error);\n\t\t\tthrow f.failure;\n\t\t}\n\t});\n\treturn f.chain;\n}\n\n/** A sync that has not heard anything yet, from `token`. */\nfunction following(token: ResumeToken): Following {\n\treturn {\n\t\tpending: new Map(),\n\t\tlast: undefined,\n\t\tsaved: token,\n\t\tinFlight: 0,\n\t\ttimer: undefined,\n\t\tbeat: undefined,\n\t\tstopLease: noop,\n\t\theard: false,\n\t\tchain: Promise.resolve(),\n\t\tdone: false,\n\t\tfailure: undefined,\n\t};\n}\n\n/** Stops everything a running sync does on its own. */\nfunction stopFollowing(ctx: SyncContext, f: Following): void {\n\tf.done = true;\n\tclearTimeout(f.timer);\n\tclearInterval(f.beat);\n\tf.stopLease();\n\tctx.follower.running = false;\n}\n\n/**\n * What `closed` settles to once the stream stops: the reason, or the failure\n * that stopped it — after the last queued flush, and with the lease let go.\n */\nfunction settle(\n\tctx: SyncContext,\n\tf: Following,\n\tsubscription: ChangeSubscription,\n\tlease: HeldLease,\n): Promise<CloseReason> {\n\tconst letGo = () => (f.heard ? release(ctx, lease) : Promise.resolve());\n\treturn subscription.closed.then(\n\t\tasync (reason) => {\n\t\t\tstopFollowing(ctx, f);\n\t\t\ttry {\n\t\t\t\t// A flush the timer or the beat had queued would otherwise record\n\t\t\t\t// a point after this.\n\t\t\t\tawait f.chain.catch(noop);\n\t\t\t\tif (f.failure) throw f.failure;\n\t\t\t\t// The collection is gone, and so is the point the stream stopped\n\t\t\t\t// at: what is recorded is older, and every later start would stop\n\t\t\t\t// here again. Forgetting it makes the next one reindex.\n\t\t\t\tif (reason === 'invalidated') await clearState(ctx);\n\t\t\t\treturn reason;\n\t\t\t} finally {\n\t\t\t\tawait letGo();\n\t\t\t}\n\t\t},\n\t\tasync (error: unknown) => {\n\t\t\tstopFollowing(ctx, f);\n\t\t\tawait letGo();\n\t\t\tthrow f.failure ?? failed(ctx.name, 'following changes', error);\n\t\t},\n\t);\n}\n\nexport async function open(\n\tctx: SyncContext,\n\ttoken: ResumeToken,\n\tlease: HeldLease,\n): Promise<RunningSearchSync> {\n\tconst f = following(token);\n\tconst handle = async (change: ChangeOf<AnyCollectionDefinition>) => {\n\t\tif (f.failure) throw f.failure;\n\t\tf.inFlight += 1;\n\t\tlet entry: Entry;\n\t\ttry {\n\t\t\tconst document =\n\t\t\t\tchange.type === 'delete' ? undefined : (change.document as Doc);\n\t\t\tentry = await entryOf(ctx, change.id, document);\n\t\t} catch (error) {\n\t\t\t// Kept, so `flush` and `close` report it as `closed` does.\n\t\t\tf.failure = failed(ctx.name, 'following changes', error);\n\t\t\tthrow f.failure;\n\t\t} finally {\n\t\t\tf.inFlight -= 1;\n\t\t}\n\t\tf.pending.set(entry.key, entry);\n\t\tf.last = change.resumeToken;\n\t\tif (f.pending.size >= ctx.batchSize) {\n\t\t\tawait flush(ctx, f, subscription);\n\t\t} else if (!f.timer) {\n\t\t\tf.timer = setTimeout(() => {\n\t\t\t\tflush(ctx, f, subscription).catch(() => subscription.close());\n\t\t\t}, ctx.flushIntervalMs);\n\t\t}\n\t};\n\tconst subscription = ctx.collection.onChange(handle, { startAfter: token });\n\tf.beat = setInterval(() => {\n\t\tflush(ctx, f, subscription).catch(() => subscription.close());\n\t}, ctx.positionIntervalMs);\n\t// A lease found gone means another process may be following this name:\n\t// stop at once, before anything else is sent or recorded beside it.\n\tf.stopLease = keepLease(ctx, lease, () => {\n\t\tif (f.failure) return;\n\t\tf.failure = leaseLost(ctx);\n\t\tvoid subscription.close();\n\t});\n\tconst closed = settle(ctx, f, subscription, lease);\n\ttry {\n\t\tawait subscription.ready;\n\t} catch (error) {\n\t\tstopFollowing(ctx, f);\n\t\tclosed.catch(noop);\n\t\tthrow error;\n\t}\n\tf.heard = true;\n\tctx.follower.running = true;\n\tconst close = async () => {\n\t\ttry {\n\t\t\tawait flush(ctx, f, subscription);\n\t\t} finally {\n\t\t\tawait subscription.close();\n\t\t\t// The lease is let go in `closed`'s handler: waited for, so a\n\t\t\t// `reindex` or a `start` right after this finds the name free. Not\n\t\t\t// from inside a change being handled, which `closed` waits for.\n\t\t\tif (f.inFlight === 0) await closed.then(noop, noop);\n\t\t}\n\t};\n\treturn {\n\t\tready: subscription.ready,\n\t\tclosed,\n\t\tflush: () => flush(ctx, f, subscription),\n\t\tclose,\n\t\t[Symbol.asyncDispose]: close,\n\t};\n}\n",
14
- "import type { ResumeToken } from '@nxgt/mongo';\nimport type { SyncContext } from './context';\nimport { failed, SearchSyncError } from './errors';\nimport { open } from './follow';\nimport {\n\tacquire,\n\tconfirmLease,\n\ttype HeldLease,\n\tkeepLease,\n\trelease,\n} from './lease';\nimport { reindexHeld } from './reindex';\nimport { checkIdle } from './running';\nimport { readState } from './state';\nimport type { RunningSearchSync } from './types';\n\n/** The server no longer has the history a token points into. */\nfunction historyLost(error: unknown): boolean {\n\tconst code = (error as { serverCode?: number } | undefined)?.serverCode;\n\treturn code === 286 || code === 280;\n}\n\n/**\n * Follows the collection from its recorded point — after a reindex when\n * nothing is recorded, or when the point is older than the server's history\n * and `onHistoryLost` allows it.\n */\nexport async function start(ctx: SyncContext): Promise<RunningSearchSync> {\n\tcheckIdle(ctx, 'start it twice');\n\t// Before anything else, the first reindex included: two processes\n\t// starting at once would otherwise both reindex, and both follow.\n\tconst lease = await acquire(ctx, 'start it');\n\t// Renewed while it reindexes, which can outlast `leaseMs`; the follower\n\t// renews it on its own once it is open.\n\tconst stop = keepLease(ctx, lease, () => undefined);\n\ttry {\n\t\tconst running = await startHeld(ctx, lease);\n\t\tstop();\n\t\treturn running;\n\t} catch (error) {\n\t\tstop();\n\t\tawait release(ctx, lease);\n\t\tthrow error;\n\t}\n}\n\n/** Opens the follower, once the server confirms the lease is still held. */\nasync function openHeld(\n\tctx: SyncContext,\n\ttoken: ResumeToken,\n\tlease: HeldLease,\n): Promise<RunningSearchSync> {\n\tawait confirmLease(ctx, lease);\n\treturn open(ctx, token, lease);\n}\n\nasync function startHeld(\n\tctx: SyncContext,\n\tlease: HeldLease,\n): Promise<RunningSearchSync> {\n\tlet state = await readState(ctx);\n\tif (!state) {\n\t\tawait reindexHeld(ctx, lease);\n\t\tstate = await readState(ctx);\n\t}\n\ttry {\n\t\treturn await openHeld(\n\t\t\tctx,\n\t\t\t(state as { resumeToken: ResumeToken }).resumeToken,\n\t\t\tlease,\n\t\t);\n\t} catch (error) {\n\t\tif (!historyLost(error)) throw failed(ctx.name, 'starting', error);\n\t\tif (ctx.onHistoryLost === 'fail') {\n\t\t\tthrow new SearchSyncError(\n\t\t\t\t`Search sync \"${ctx.name}\" was last at a point the server's change ` +\n\t\t\t\t\t'history no longer reaches. Reindex it, or start it with ' +\n\t\t\t\t\t\"onHistoryLost: 'reindex'.\",\n\t\t\t\t{ code: 'HISTORY_LOST', sync: ctx.name, cause: error },\n\t\t\t);\n\t\t}\n\t\tawait reindexHeld(ctx, lease);\n\t\tconst fresh = (await readState(ctx)) as { resumeToken: ResumeToken };\n\t\ttry {\n\t\t\treturn await openHeld(ctx, fresh.resumeToken, lease);\n\t\t} catch (again) {\n\t\t\tthrow failed(ctx.name, 'starting', again);\n\t\t}\n\t}\n}\n",
15
- "import type { AnyIndexDefinition } from '@nxgt/meilisearch';\nimport type { AnyCollectionDefinition } from '@nxgt/mongo';\nimport { createContext } from './context';\nimport { reindex } from './reindex';\nimport { start } from './start';\nimport { readState } from './state';\nimport type { SearchSync, SearchSyncOptions } from './types';\n\n/**\n * Keeps an index in step with a collection.\n *\n * ```ts\n * const usersSearch = createSearchSync({\n * \tcollection: getCollection(db, users),\n * \tindex: bindIndex(meili, userIndex),\n * \ttransform: (user) => (user.active ? { id: user.id, name: user.name } : null),\n * });\n * const running = await usersSearch.start(); // reindexes the first time\n * // …\n * await running.close();\n * ```\n *\n * Nothing is sent before `reindex` or `start`.\n */\nexport function createSearchSync<\n\tC extends AnyCollectionDefinition,\n\tI extends AnyIndexDefinition,\n>(options: SearchSyncOptions<C, I>): SearchSync {\n\t// The one place the caller's types are let go: a method taking `IdOf<C>`\n\t// is not one taking any id, so the widening cannot be implicit. Below\n\t// here, documents are records and ids are values.\n\tconst ctx = createContext(\n\t\toptions as unknown as SearchSyncOptions<\n\t\t\tAnyCollectionDefinition,\n\t\t\tAnyIndexDefinition\n\t\t>,\n\t);\n\treturn {\n\t\tname: ctx.name,\n\t\treindex: () => reindex(ctx),\n\t\tstart: () => start(ctx),\n\t\tstate: () => readState(ctx),\n\t};\n}\n"
11
+ "import { hostname } from 'node:os';\nimport { ObjectId } from 'mongodb';\nimport type { SyncContext } from '../sync/context';\nimport { failed, SearchSyncError } from '../sync/errors';\n\n/**\n * The lease on a sync's name: one document in the state collection, whose\n * `_id` is `{ lease: <name> }` — an object, so it can never be a sync's own\n * state, whose `_id` is the name itself.\n *\n * Taking it is one atomic write whose update decides — the server, not a\n * read before a write. Every time is the server's (`$$NOW`), so hosts whose\n * clocks disagree still agree on when a lease lapses.\n *\n * Renewal, release and the holder's name follow `@nxgt/mongo`'s migration\n * lock (`migrations/lock.ts`); taking it does not — see `take`.\n */\nexport interface LeaseDocument {\n\t_id: { lease: string };\n\tholder: string;\n\tacquiredAt: Date;\n\texpiresAt: Date;\n}\n\n/** A held lease: data, and whether a renewal found it gone. */\nexport interface HeldLease {\n\treadonly holder: string;\n\tlost: boolean;\n}\n\nconst idOf = (ctx: SyncContext) => ({ lease: ctx.name });\n\n/**\n * Inserts the lease, or takes over one that lapsed, in one atomic upsert.\n *\n * The filter is `_id` alone — the server allows no `$expr` in an upsert's\n * filter — and the decision is made by the update instead: a pipeline that\n * keeps the current holder's fields while its lease runs, and writes this\n * holder's when there is none or it lapsed. What comes back says who won.\n * It is a `findAndModify`, not an `aggregate`: `@nxgt/mongo`'s migration lock\n * takes its lock with `$documents`/`$merge`, which a failpoint or a monitor\n * aimed at the change stream's `aggregate` would also catch.\n */\nasync function take(ctx: SyncContext, holder: string): Promise<boolean> {\n\tconst free = {\n\t\t$or: [\n\t\t\t{ $eq: [{ $type: '$expiresAt' }, 'missing'] },\n\t\t\t{ $lte: ['$expiresAt', '$$NOW'] },\n\t\t],\n\t};\n\tconst pick = (mine: unknown, theirs: string) => ({\n\t\t$cond: [free, mine, theirs],\n\t});\n\ttry {\n\t\tconst after = await ctx.leases.findOneAndUpdate(\n\t\t\t{ _id: idOf(ctx) },\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\t$set: {\n\t\t\t\t\t\tholder: pick({ $literal: holder }, '$holder'),\n\t\t\t\t\t\tacquiredAt: pick('$$NOW', '$acquiredAt'),\n\t\t\t\t\t\texpiresAt: pick({ $add: ['$$NOW', ctx.leaseMs] }, '$expiresAt'),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t\t{ upsert: true, returnDocument: 'after' },\n\t\t);\n\t\treturn after?.holder === holder;\n\t} catch (error) {\n\t\t// Two upserts racing to insert the same `_id`: the other one won.\n\t\tif ((error as { code?: number }).code === 11000) return false;\n\t\tthrow error;\n\t}\n}\n\n/**\n * Takes the lease, or throws `RUNNING` naming who holds it and until when —\n * in the message and as `holder` and `expiresAt`, so a standby can wait for\n * that moment rather than a fixed time. The same code a second `start` in\n * this process gets, since it means the same thing: this name is being\n * followed.\n */\nexport async function acquire(\n\tctx: SyncContext,\n\tdoing: string,\n): Promise<HeldLease> {\n\tconst holder = `${hostname()}:${process.pid}:${new ObjectId().toHexString()}`;\n\tlet current: LeaseDocument | null = null;\n\ttry {\n\t\t// Twice at most: a holder that let go between the take and the read\n\t\t// left the name free, and refusing it then would be wrong.\n\t\tfor (let attempt = 0; attempt < 2; attempt += 1) {\n\t\t\tif (await take(ctx, holder)) return { holder, lost: false };\n\t\t\tcurrent = await ctx.leases.findOne({ _id: idOf(ctx) });\n\t\t\tif (current) break;\n\t\t}\n\t} catch (error) {\n\t\tthrow failed(ctx.name, 'taking its lease', error);\n\t}\n\t// Each only when the document holds what it should; `expiresAt` only\n\t// beside a holder, as the error's own documentation promises.\n\tconst until =\n\t\tcurrent?.expiresAt instanceof Date ? current.expiresAt : undefined;\n\tconst theirs =\n\t\ttypeof current?.holder === 'string' ? current.holder : undefined;\n\tconst expiresAt = theirs === undefined ? undefined : until;\n\tthrow new SearchSyncError(\n\t\t`Search sync \"${ctx.name}\" is held by ${theirs ?? 'another process'} ` +\n\t\t\t`until ${until?.toISOString() ?? 'it lets go'}: ` +\n\t\t\t`wait for it to close, or for its lease to lapse, before you ${doing}.`,\n\t\t{\n\t\t\tcode: 'RUNNING',\n\t\t\tsync: ctx.name,\n\t\t\tholder: theirs,\n\t\t\texpiresAt,\n\t\t},\n\t);\n}\n\n/**\n * Pushes the lease's end back by `leaseMs`. A renewal that finds another\n * holder marks the lease lost; one that does not reach the server is tried\n * again at the next beat, and the lease lapses only if none reaches it for a\n * whole `leaseMs`.\n */\nexport async function renew(ctx: SyncContext, lease: HeldLease): Promise<void> {\n\ttry {\n\t\tconst result = await ctx.leases.updateOne(\n\t\t\t{ _id: idOf(ctx), holder: lease.holder },\n\t\t\t[{ $set: { expiresAt: { $add: ['$$NOW', ctx.leaseMs] } } }],\n\t\t);\n\t\tif (result.matchedCount === 0) lease.lost = true;\n\t} catch {\n\t\t// Tried again at the next beat.\n\t}\n}\n\n/**\n * Renews the lease now, and throws `LEASE_LOST` if it is someone else's: the\n * check before a step that must not run beside another holder.\n */\nexport async function confirmLease(\n\tctx: SyncContext,\n\tlease: HeldLease,\n): Promise<void> {\n\tlet result: { matchedCount: number };\n\ttry {\n\t\tresult = await ctx.leases.updateOne(\n\t\t\t{ _id: idOf(ctx), holder: lease.holder },\n\t\t\t[{ $set: { expiresAt: { $add: ['$$NOW', ctx.leaseMs] } } }],\n\t\t);\n\t} catch (error) {\n\t\t// Unlike a timed renewal, not tried again later: what comes next must\n\t\t// not run on a lease nobody could confirm.\n\t\tthrow failed(ctx.name, 'checking its lease', error);\n\t}\n\tif (result.matchedCount === 0) lease.lost = true;\n\tif (lease.lost) throw leaseLost(ctx);\n}\n\n/** Lets go of it, if it is still this holder's. One that fails lapses. */\nexport async function release(\n\tctx: SyncContext,\n\tlease: HeldLease,\n): Promise<void> {\n\tawait ctx.leases\n\t\t.deleteOne({ _id: idOf(ctx), holder: lease.holder })\n\t\t.catch(() => undefined);\n}\n\n/** The error a sync stops with once its lease is someone else's. */\nexport function leaseLost(ctx: SyncContext): SearchSyncError {\n\treturn new SearchSyncError(\n\t\t`Search sync \"${ctx.name}\" lost its lease: another process holds the ` +\n\t\t\t'name now, or the lease was removed (it lapses when not renewed ' +\n\t\t\t`within ${ctx.leaseMs} ms). It stopped rather than run beside it.`,\n\t\t{ code: 'LEASE_LOST', sync: ctx.name },\n\t);\n}\n\n/**\n * Renews the lease every third of `leaseMs`, and calls `onLost` once when a\n * renewal finds it someone else's. Gives back what stops the renewals.\n */\nexport function keepLease(\n\tctx: SyncContext,\n\tlease: HeldLease,\n\tonLost: () => void,\n): () => void {\n\tlet stopped = false;\n\tconst beat = setInterval(\n\t\t() => {\n\t\t\tvoid renew(ctx, lease).then(() => {\n\t\t\t\t// A renewal still in flight when it was stopped reports nothing.\n\t\t\t\tif (lease.lost && !stopped) {\n\t\t\t\t\tclearInterval(beat);\n\t\t\t\t\tonLost();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tMath.max(1, Math.floor(ctx.leaseMs / 3)),\n\t);\n\treturn () => {\n\t\tstopped = true;\n\t\tclearInterval(beat);\n\t};\n}\n\n/**\n * Runs `fn` holding the lease, renewed every third of `leaseMs`. `fn` checks\n * it is still held where that matters (`confirmLease`).\n */\nexport async function withLease<T>(\n\tctx: SyncContext,\n\tdoing: string,\n\tfn: (lease: HeldLease) => Promise<T>,\n): Promise<T> {\n\tconst lease = await acquire(ctx, doing);\n\tconst stop = keepLease(ctx, lease, () => undefined);\n\ttry {\n\t\treturn await fn(lease);\n\t} finally {\n\t\tstop();\n\t\tawait release(ctx, lease);\n\t}\n}\n",
12
+ "import type { AnyIndexDefinition, TypedIndex } from '@nxgt/meilisearch';\nimport type { AnyCollectionDefinition, TypedCollection } from '@nxgt/mongo';\nimport type { Collection } from 'mongodb';\nimport type { LeaseDocument } from '../run/lease';\nimport type { SearchSyncOptions, SearchSyncState } from './types';\n\n/** Loosely typed: this layer works on any documents; the public types are what callers see. */\nexport type Doc = Record<string, unknown>;\n\n/**\n * What a sync works from, resolved once. Data, and the caller's own two\n * functions as they were given.\n */\nexport interface SyncContext {\n\treadonly name: string;\n\treadonly collection: TypedCollection<AnyCollectionDefinition>;\n\treadonly index: TypedIndex<AnyIndexDefinition>;\n\treadonly primaryKey: string;\n\treadonly state: Collection<SearchSyncState>;\n\t/** The same collection, for the lease documents it also holds. */\n\treadonly leases: Collection<LeaseDocument>;\n\t/** How long a lease lasts unrenewed, in ms. */\n\treadonly leaseMs: number;\n\treadonly batchSize: number;\n\treadonly flushIntervalMs: number;\n\treadonly positionIntervalMs: number;\n\treadonly pageSize: number;\n\treadonly onHistoryLost: 'reindex' | 'fail';\n\treadonly transform: (document: Doc) => unknown;\n\treadonly toIndexId: (id: unknown) => unknown;\n\t/**\n\t * Whether this sync is following changes in this process. A reindex\n\t * beside its own follower would remove what the follower has just\n\t * indexed, and the follower would never send it again.\n\t */\n\treadonly follower: { running: boolean };\n}\n\nconst DEFAULT_STATE_COLLECTION = 'nxgt_search_sync';\n\nfunction positive(option: string, value: number | undefined, fallback: number) {\n\tconst resolved = value ?? fallback;\n\tif (!Number.isInteger(resolved) || resolved < 1) {\n\t\tthrow new TypeError(\n\t\t\t`createSearchSync: ${option} must be a whole number above 0, not ${String(value)}`,\n\t\t);\n\t}\n\treturn resolved;\n}\n\nexport function createContext(\n\toptions: SearchSyncOptions<AnyCollectionDefinition, AnyIndexDefinition>,\n): SyncContext {\n\tconst { collection, index } = options;\n\tif (typeof options.transform !== 'function') {\n\t\tthrow new TypeError('createSearchSync: transform must be a function');\n\t}\n\tconst name = options.name ?? `${collection.collectionName}:${index.uid}`;\n\tif (name === '') {\n\t\tthrow new TypeError('createSearchSync: name must not be empty');\n\t}\n\tconst flushIntervalMs = options.flushIntervalMs ?? 1000;\n\tif (!Number.isInteger(flushIntervalMs) || flushIntervalMs < 0) {\n\t\tthrow new TypeError(\n\t\t\t`createSearchSync: flushIntervalMs must be a whole number of milliseconds, not ${String(options.flushIntervalMs)}`,\n\t\t);\n\t}\n\tconst stateCollection = options.stateCollection ?? DEFAULT_STATE_COLLECTION;\n\treturn {\n\t\tname,\n\t\tcollection,\n\t\tindex,\n\t\tprimaryKey: index.definition.primaryKey as string,\n\t\tstate: collection.db.collection<SearchSyncState>(stateCollection),\n\t\tleases: collection.db.collection<LeaseDocument>(stateCollection),\n\t\tleaseMs: positive('leaseMs', options.leaseMs, 30_000),\n\t\tbatchSize: positive('batchSize', options.batchSize, 500),\n\t\tpositionIntervalMs: positive(\n\t\t\t'positionIntervalMs',\n\t\t\toptions.positionIntervalMs,\n\t\t\t60_000,\n\t\t),\n\t\tflushIntervalMs,\n\t\tpageSize: positive('pageSize', options.pageSize, 100),\n\t\tonHistoryLost: options.onHistoryLost ?? 'reindex',\n\t\ttransform: options.transform as (document: Doc) => unknown,\n\t\ttoIndexId: (options.toIndexId ?? String) as (id: unknown) => unknown,\n\t\tfollower: { running: false },\n\t};\n}\n",
13
+ "import type {\n\tAnyCollectionDefinition,\n\tChangeOf,\n\tChangeSubscription,\n\tCloseReason,\n\tResumeToken,\n} from '@nxgt/mongo';\nimport { send } from '../sync/batch';\nimport type { Doc, SyncContext } from '../sync/context';\nimport { type Entry, entryOf } from '../sync/documents';\nimport { failed, type SearchSyncError } from '../sync/errors';\nimport { clearState, saveState } from '../sync/state';\nimport type { RunningSearchSync } from '../sync/types';\nimport { type HeldLease, keepLease, leaseLost, release } from './lease';\n\nconst noop = () => undefined;\n\n/** What a running sync holds between changes. */\ninterface Following {\n\treadonly pending: Map<string, Entry>;\n\t/** The token of the last change in `pending`. */\n\tlast: ResumeToken | undefined;\n\tsaved: ResumeToken;\n\t/** Changes whose handler has not queued them yet. */\n\tinFlight: number;\n\ttimer: ReturnType<typeof setTimeout> | undefined;\n\t/** Records where the stream is while nothing is being sent. */\n\tbeat: ReturnType<typeof setInterval> | undefined;\n\t/** Stops renewing the lease on the sync's name. */\n\tstopLease: () => void;\n\t/**\n\t * The stream was heard. Only then does `closed` let go of the lease: a\n\t * stream that never got ready leaves it to `start`, which may reindex and\n\t * open again on it after a lost history.\n\t */\n\theard: boolean;\n\t/** Flushes run one after the other. */\n\tchain: Promise<void>;\n\t/** The stream has stopped: what is queued no longer records anything. */\n\tdone: boolean;\n\tfailure: SearchSyncError | undefined;\n}\n\n/**\n * Sends what is pending, then records the token that covers it — or, with\n * nothing to send and no change being handled, where the stream is: a quiet\n * collection would otherwise keep a resume point until the server's history\n * no longer reaches it.\n */\nfunction flush(\n\tctx: SyncContext,\n\tf: Following,\n\tsubscription: ChangeSubscription,\n): Promise<void> {\n\tf.chain = f.chain.then(async () => {\n\t\tif (f.failure) throw f.failure;\n\t\tif (f.done) return;\n\t\tclearTimeout(f.timer);\n\t\tf.timer = undefined;\n\t\tconst entries = [...f.pending.values()];\n\t\tconst upTo = f.last;\n\t\tf.pending.clear();\n\t\ttry {\n\t\t\tif (entries.length > 0) await send(ctx, entries);\n\t\t\tconst idle = entries.length === 0 && f.inFlight === 0;\n\t\t\tconst at = upTo ?? (idle ? subscription.position : undefined);\n\t\t\tif (at && at !== f.saved) {\n\t\t\t\tawait saveState(ctx, at, false);\n\t\t\t\tf.saved = at;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tf.failure ??= failed(ctx.name, 'sending changes', error);\n\t\t\tthrow f.failure;\n\t\t}\n\t});\n\treturn f.chain;\n}\n\n/** A sync that has not heard anything yet, from `token`. */\nfunction following(token: ResumeToken): Following {\n\treturn {\n\t\tpending: new Map(),\n\t\tlast: undefined,\n\t\tsaved: token,\n\t\tinFlight: 0,\n\t\ttimer: undefined,\n\t\tbeat: undefined,\n\t\tstopLease: noop,\n\t\theard: false,\n\t\tchain: Promise.resolve(),\n\t\tdone: false,\n\t\tfailure: undefined,\n\t};\n}\n\n/** Stops everything a running sync does on its own. */\nfunction stopFollowing(ctx: SyncContext, f: Following): void {\n\tf.done = true;\n\tclearTimeout(f.timer);\n\tclearInterval(f.beat);\n\tf.stopLease();\n\tctx.follower.running = false;\n}\n\n/**\n * What `closed` settles to once the stream stops: the reason, or the failure\n * that stopped it — after the last queued flush, and with the lease let go.\n */\nfunction settle(\n\tctx: SyncContext,\n\tf: Following,\n\tsubscription: ChangeSubscription,\n\tlease: HeldLease,\n): Promise<CloseReason> {\n\tconst letGo = () => (f.heard ? release(ctx, lease) : Promise.resolve());\n\treturn subscription.closed.then(\n\t\tasync (reason) => {\n\t\t\tstopFollowing(ctx, f);\n\t\t\ttry {\n\t\t\t\t// A flush the timer or the beat had queued would otherwise record\n\t\t\t\t// a point after this.\n\t\t\t\tawait f.chain.catch(noop);\n\t\t\t\tif (f.failure) throw f.failure;\n\t\t\t\t// The collection is gone, and so is the point the stream stopped\n\t\t\t\t// at: what is recorded is older, and every later start would stop\n\t\t\t\t// here again. Forgetting it makes the next one reindex.\n\t\t\t\tif (reason === 'invalidated') await clearState(ctx);\n\t\t\t\treturn reason;\n\t\t\t} finally {\n\t\t\t\tawait letGo();\n\t\t\t}\n\t\t},\n\t\tasync (error: unknown) => {\n\t\t\tstopFollowing(ctx, f);\n\t\t\tawait letGo();\n\t\t\tthrow f.failure ?? failed(ctx.name, 'following changes', error);\n\t\t},\n\t);\n}\n\nexport async function open(\n\tctx: SyncContext,\n\ttoken: ResumeToken,\n\tlease: HeldLease,\n): Promise<RunningSearchSync> {\n\tconst f = following(token);\n\tconst handle = async (change: ChangeOf<AnyCollectionDefinition>) => {\n\t\tif (f.failure) throw f.failure;\n\t\tf.inFlight += 1;\n\t\tlet entry: Entry;\n\t\ttry {\n\t\t\tconst document =\n\t\t\t\tchange.type === 'delete' ? undefined : (change.document as Doc);\n\t\t\tentry = await entryOf(ctx, change.id, document);\n\t\t} catch (error) {\n\t\t\t// Kept, so `flush` and `close` report it as `closed` does.\n\t\t\tf.failure = failed(ctx.name, 'following changes', error);\n\t\t\tthrow f.failure;\n\t\t} finally {\n\t\t\tf.inFlight -= 1;\n\t\t}\n\t\tf.pending.set(entry.key, entry);\n\t\tf.last = change.resumeToken;\n\t\tif (f.pending.size >= ctx.batchSize) {\n\t\t\tawait flush(ctx, f, subscription);\n\t\t} else if (!f.timer) {\n\t\t\tf.timer = setTimeout(() => {\n\t\t\t\tflush(ctx, f, subscription).catch(() => subscription.close());\n\t\t\t}, ctx.flushIntervalMs);\n\t\t}\n\t};\n\tconst subscription = ctx.collection.onChange(handle, { startAfter: token });\n\tf.beat = setInterval(() => {\n\t\tflush(ctx, f, subscription).catch(() => subscription.close());\n\t}, ctx.positionIntervalMs);\n\t// A lease found gone means another process may be following this name:\n\t// stop at once, before anything else is sent or recorded beside it.\n\tf.stopLease = keepLease(ctx, lease, () => {\n\t\tif (f.failure) return;\n\t\tf.failure = leaseLost(ctx);\n\t\tvoid subscription.close();\n\t});\n\tconst closed = settle(ctx, f, subscription, lease);\n\ttry {\n\t\tawait subscription.ready;\n\t} catch (error) {\n\t\tstopFollowing(ctx, f);\n\t\tclosed.catch(noop);\n\t\tthrow error;\n\t}\n\tf.heard = true;\n\tctx.follower.running = true;\n\tconst close = async () => {\n\t\ttry {\n\t\t\tawait flush(ctx, f, subscription);\n\t\t} finally {\n\t\t\tawait subscription.close();\n\t\t\t// The lease is let go in `closed`'s handler: waited for, so a\n\t\t\t// `reindex` or a `start` right after this finds the name free. Not\n\t\t\t// from inside a change being handled, which `closed` waits for.\n\t\t\tif (f.inFlight === 0) await closed.then(noop, noop);\n\t\t}\n\t};\n\treturn {\n\t\tready: subscription.ready,\n\t\tclosed,\n\t\tflush: () => flush(ctx, f, subscription),\n\t\tclose,\n\t\t[Symbol.asyncDispose]: close,\n\t};\n}\n",
14
+ "import type { ResumeToken } from '@nxgt/mongo';\nimport { open } from '../run/follow';\nimport {\n\tacquire,\n\tconfirmLease,\n\ttype HeldLease,\n\tkeepLease,\n\trelease,\n} from '../run/lease';\nimport { reindexHeld } from '../run/reindex';\nimport type { SyncContext } from './context';\nimport { failed, SearchSyncError } from './errors';\nimport { checkIdle } from './running';\nimport { readState } from './state';\nimport type { RunningSearchSync } from './types';\n\n/** The server no longer has the history a token points into. */\nfunction historyLost(error: unknown): boolean {\n\tconst code = (error as { serverCode?: number } | undefined)?.serverCode;\n\treturn code === 286 || code === 280;\n}\n\n/**\n * Follows the collection from its recorded point — after a reindex when\n * nothing is recorded, or when the point is older than the server's history\n * and `onHistoryLost` allows it.\n */\nexport async function start(ctx: SyncContext): Promise<RunningSearchSync> {\n\tcheckIdle(ctx, 'start it twice');\n\t// Before anything else, the first reindex included: two processes\n\t// starting at once would otherwise both reindex, and both follow.\n\tconst lease = await acquire(ctx, 'start it');\n\t// Renewed while it reindexes, which can outlast `leaseMs`; the follower\n\t// renews it on its own once it is open.\n\tconst stop = keepLease(ctx, lease, () => undefined);\n\ttry {\n\t\tconst running = await startHeld(ctx, lease);\n\t\tstop();\n\t\treturn running;\n\t} catch (error) {\n\t\tstop();\n\t\tawait release(ctx, lease);\n\t\tthrow error;\n\t}\n}\n\n/** Opens the follower, once the server confirms the lease is still held. */\nasync function openHeld(\n\tctx: SyncContext,\n\ttoken: ResumeToken,\n\tlease: HeldLease,\n): Promise<RunningSearchSync> {\n\tawait confirmLease(ctx, lease);\n\treturn open(ctx, token, lease);\n}\n\nasync function startHeld(\n\tctx: SyncContext,\n\tlease: HeldLease,\n): Promise<RunningSearchSync> {\n\tlet state = await readState(ctx);\n\tif (!state) {\n\t\tawait reindexHeld(ctx, lease);\n\t\tstate = await readState(ctx);\n\t}\n\ttry {\n\t\treturn await openHeld(\n\t\t\tctx,\n\t\t\t(state as { resumeToken: ResumeToken }).resumeToken,\n\t\t\tlease,\n\t\t);\n\t} catch (error) {\n\t\tif (!historyLost(error)) throw failed(ctx.name, 'starting', error);\n\t\tif (ctx.onHistoryLost === 'fail') {\n\t\t\tthrow new SearchSyncError(\n\t\t\t\t`Search sync \"${ctx.name}\" was last at a point the server's change ` +\n\t\t\t\t\t'history no longer reaches. Reindex it, or start it with ' +\n\t\t\t\t\t\"onHistoryLost: 'reindex'.\",\n\t\t\t\t{ code: 'HISTORY_LOST', sync: ctx.name, cause: error },\n\t\t\t);\n\t\t}\n\t\tawait reindexHeld(ctx, lease);\n\t\tconst fresh = (await readState(ctx)) as { resumeToken: ResumeToken };\n\t\ttry {\n\t\t\treturn await openHeld(ctx, fresh.resumeToken, lease);\n\t\t} catch (again) {\n\t\t\tthrow failed(ctx.name, 'starting', again);\n\t\t}\n\t}\n}\n",
15
+ "import type { AnyIndexDefinition } from '@nxgt/meilisearch';\nimport type { AnyCollectionDefinition } from '@nxgt/mongo';\nimport { reindex } from '../run/reindex';\nimport { createContext } from './context';\nimport { start } from './start';\nimport { readState } from './state';\nimport type { SearchSync, SearchSyncOptions } from './types';\n\n/**\n * Keeps an index in step with a collection.\n *\n * ```ts\n * const usersSearch = createSearchSync({\n * \tcollection: getCollection(db, users),\n * \tindex: bindIndex(meili, userIndex),\n * \ttransform: (user) => (user.active ? { id: user.id, name: user.name } : null),\n * });\n * const running = await usersSearch.start(); // reindexes the first time\n * // …\n * await running.close();\n * ```\n *\n * Nothing is sent before `reindex` or `start`.\n */\nexport function createSearchSync<\n\tC extends AnyCollectionDefinition,\n\tI extends AnyIndexDefinition,\n>(options: SearchSyncOptions<C, I>): SearchSync {\n\t// The one place the caller's types are let go: a method taking `IdOf<C>`\n\t// is not one taking any id, so the widening cannot be implicit. Below\n\t// here, documents are records and ids are values.\n\tconst ctx = createContext(\n\t\toptions as unknown as SearchSyncOptions<\n\t\t\tAnyCollectionDefinition,\n\t\t\tAnyIndexDefinition\n\t\t>,\n\t);\n\treturn {\n\t\tname: ctx.name,\n\t\treindex: () => reindex(ctx),\n\t\tstart: () => start(ctx),\n\t\tstate: () => readState(ctx),\n\t};\n}\n",
16
+ "import type { SyncReport, TypedIndex } from '@nxgt/meilisearch';\nimport type {\n\tAnyCollectionDefinition,\n\tMongo,\n\tTypedCollection,\n} from '@nxgt/mongo';\nimport { createSearchSync } from '../sync/create-search-sync';\nimport type {\n\tReindexReport,\n\tRunningSearchSync,\n\tSearchSync,\n\tSearchSyncState,\n} from '../sync/types';\nimport type {\n\tByKey,\n\tIndexMap,\n\tRunningSearchSyncs,\n\tSearchSyncs,\n\tSearchSyncsConfig,\n} from './types';\n\n/** The Mongo's sole scope, or a refusal naming what to do instead. */\nfunction soleScope(mongo: Mongo<unknown>): Record<string, unknown> {\n\tconst names = Object.keys(mongo.databases);\n\tif (names.length !== 1) {\n\t\tthrow new TypeError(\n\t\t\t'createSearchSyncs: this Mongo holds ' +\n\t\t\t\t`${names.length} databases (${names.join(', ')}), and ` +\n\t\t\t\t'createSearchSyncs follows the collections of one. Build one ' +\n\t\t\t\t'`createSearchSyncs` per database, from a Mongo that wires that ' +\n\t\t\t\t'database alone',\n\t\t);\n\t}\n\treturn mongo.db as unknown as Record<string, unknown>;\n}\n\n/** The collection a key names, refused by name when the Mongo wires none. */\nfunction collectionAt(\n\tscope: Record<string, unknown>,\n\tkey: string,\n): TypedCollection<AnyCollectionDefinition> {\n\tconst collection = scope[key];\n\t// A `Db` answers to its own members, so a key that is one would give\n\t// something that is not a collection rather than `undefined`. A GridFS\n\t// bucket the Mongo wires has a `definition` too, so it is the definition's\n\t// shape that decides: a collection's has a schema, a bucket's has none.\n\tconst definition =\n\t\ttypeof collection === 'object' && collection !== null\n\t\t\t? (collection as { definition?: unknown }).definition\n\t\t\t: undefined;\n\tif (\n\t\ttypeof definition !== 'object' ||\n\t\tdefinition === null ||\n\t\t!('schema' in definition)\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`createSearchSyncs: this Mongo wires no collection called \"${key}\"`,\n\t\t);\n\t}\n\treturn collection as TypedCollection<AnyCollectionDefinition>;\n}\n\n/**\n * The search syncs: one `createSearchSync` per entry, over the collections\n * the Mongo already holds.\n *\n * It is an object of its own rather than something added to the Mongo: the\n * Mongo stays as `openMongo` returned it, and each of the two is closed by\n * whoever opened it. The search syncs close only what they started — the\n * collections, the client and the database are the Mongo's.\n */\nexport function createSearchSyncs<C, const I extends IndexMap<I>>(\n\tmongo: Mongo<C>,\n\tconfig: SearchSyncsConfig<C, I>,\n): SearchSyncs<I> {\n\tconst scope = soleScope(mongo as Mongo<unknown>);\n\tconst keys = Object.keys(config) as (keyof I & string)[];\n\n\tconst syncs = {} as Record<keyof I & string, SearchSync>;\n\tconst indexes = {} as Record<keyof I & string, TypedIndex<never>>;\n\tfor (const key of keys) {\n\t\tconst entry = config[key] as unknown as Record<string, unknown>;\n\t\tindexes[key] = entry['index'] as TypedIndex<never>;\n\t\tsyncs[key] = createSearchSync({\n\t\t\t...entry,\n\t\t\tcollection: collectionAt(scope, key),\n\t\t} as Parameters<typeof createSearchSync>[0]);\n\t}\n\n\treturn {\n\t\tsyncs: syncs as ByKey<I, SearchSync>,\n\n\t\tasync state() {\n\t\t\tconst state = {} as Record<keyof I & string, SearchSyncState | undefined>;\n\t\t\tfor (const key of keys) state[key] = await syncs[key].state();\n\t\t\treturn state as ByKey<I, SearchSyncState | undefined>;\n\t\t},\n\n\t\tasync syncIndexes(options) {\n\t\t\tconst reports = {} as Record<keyof I & string, SyncReport>;\n\t\t\tfor (const key of keys) reports[key] = await indexes[key].sync(options);\n\t\t\treturn reports as ByKey<I, SyncReport>;\n\t\t},\n\n\t\tasync reindexAll() {\n\t\t\tconst reports = {} as Record<keyof I & string, ReindexReport>;\n\t\t\tfor (const key of keys) reports[key] = await syncs[key].reindex();\n\t\t\treturn reports as ByKey<I, ReindexReport>;\n\t\t},\n\n\t\tasync start() {\n\t\t\tconst running = {} as Record<keyof I & string, RunningSearchSync>;\n\t\t\tconst started: RunningSearchSync[] = [];\n\t\t\tfor (const key of keys) {\n\t\t\t\tlet one: RunningSearchSync;\n\t\t\t\ttry {\n\t\t\t\t\tone = await syncs[key].start();\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Nothing half-started: a caller that catches this owns no sync.\n\t\t\t\t\tawait Promise.allSettled(started.map((it) => it.close()));\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\t// Taken the moment the sync exists, not once the loop is done: a\n\t\t\t\t// later `start` reindexes when its key has nothing recorded, which\n\t\t\t\t// is minutes on a real collection, and this sync is already\n\t\t\t\t// following changes throughout. A `closed` that rejects with\n\t\t\t\t// nobody listening ends the process.\n\t\t\t\tone.closed.catch(() => undefined);\n\t\t\t\trunning[key] = one;\n\t\t\t\tstarted.push(one);\n\t\t\t}\n\t\t\treturn runningSyncs(running as ByKey<I, RunningSearchSync>, started);\n\t\t},\n\t};\n}\n\n/** The started syncs: what `start` hands over. */\nfunction runningSyncs<S>(\n\trunning: ByKey<S, RunningSearchSync>,\n\tstarted: RunningSearchSync[],\n): RunningSearchSyncs<S> {\n\tlet closing: Promise<void> | undefined;\n\n\t// `closed` resolves on a clean stop and rejects on a failure. Only the\n\t// rejections are interesting here, so a resolution is turned into a\n\t// promise that never settles — `failed` is what a caller awaits.\n\t//\n\t// The sync whose rejection wins the race is kept, because that is the one\n\t// `failed` reports: `close` stays quiet about that one alone, and still\n\t// throws a *second* sync's failure, which `failed` can no longer carry.\n\tlet reported: RunningSearchSync | undefined;\n\tconst failed = Promise.race(\n\t\tstarted.map((one) =>\n\t\t\tone.closed.then(\n\t\t\t\t() => new Promise<never>(() => {}),\n\t\t\t\t(reason: unknown) => {\n\t\t\t\t\treported ??= one;\n\t\t\t\t\tthrow reason;\n\t\t\t\t},\n\t\t\t),\n\t\t),\n\t);\n\t// And `failed` itself is taken, so that a caller who never looks at it\n\t// does not end the process either.\n\tfailed.catch(() => undefined);\n\n\tconst close = () => {\n\t\tclosing ??= (async () => {\n\t\t\tlet first: unknown;\n\t\t\tlet caught = false;\n\t\t\tfor (const one of started) {\n\t\t\t\ttry {\n\t\t\t\t\tawait one.close();\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// The sync `failed` reported rejects `close` with what it\n\t\t\t\t\t// stopped on. That is `failed`'s to report, not this one's — a\n\t\t\t\t\t// caller should not have to wrap `close` to hear it twice. Any\n\t\t\t\t\t// other failure, and anything that goes wrong *while* closing,\n\t\t\t\t\t// is thrown: the first of them.\n\t\t\t\t\tif (one === reported || caught) continue;\n\t\t\t\t\tfirst = error;\n\t\t\t\t\tcaught = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (caught) throw first;\n\t\t})();\n\t\treturn closing;\n\t};\n\n\treturn {\n\t\trunning,\n\t\tfailed,\n\t\tasync flush() {\n\t\t\tfor (const one of started) await one.flush();\n\t\t},\n\t\tclose,\n\t\t[Symbol.asyncDispose]: close,\n\t};\n}\n"
16
17
  ],
17
- "mappings": "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",
18
- "debugId": "CB9FE6862D67E10C64756E2164756E21",
18
+ "mappings": "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",
19
+ "debugId": "C960A5123B6682B664756E2164756E21",
19
20
  "names": []
20
21
  }
@@ -1,6 +1,6 @@
1
1
  import type { ResumeToken } from '@nxgt/mongo';
2
- import type { SyncContext } from './context';
2
+ import type { SyncContext } from '../sync/context';
3
+ import type { RunningSearchSync } from '../sync/types';
3
4
  import { type HeldLease } from './lease';
4
- import type { RunningSearchSync } from './types';
5
5
  export declare function open(ctx: SyncContext, token: ResumeToken, lease: HeldLease): Promise<RunningSearchSync>;
6
6
  //# sourceMappingURL=follow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follow.d.ts","sourceRoot":"","sources":["../../src/run/follow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKX,WAAW,EACX,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAO,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAIxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,SAAS,CAAC;AA+HxE,wBAAsB,IAAI,CACzB,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,SAAS,GACd,OAAO,CAAC,iBAAiB,CAAC,CAkE5B"}
@@ -1,5 +1,5 @@
1
- import type { SyncContext } from './context';
2
- import { SearchSyncError } from './errors';
1
+ import type { SyncContext } from '../sync/context';
2
+ import { SearchSyncError } from '../sync/errors';
3
3
  /**
4
4
  * The lease on a sync's name: one document in the state collection, whose
5
5
  * `_id` is `{ lease: <name> }` — an object, so it can never be a sync's own
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease.d.ts","sourceRoot":"","sources":["../../src/run/lease.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAU,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,IAAI,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;CAChB;AAED,+DAA+D;AAC/D,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC;CACd;AA+CD;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC5B,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,SAAS,CAAC,CAgCpB;AAED;;;;;GAKG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7E;AAED;;;GAGG;AACH,wBAAsB,YAAY,CACjC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,SAAS,GACd,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,0EAA0E;AAC1E,wBAAsB,OAAO,CAC5B,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,SAAS,GACd,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,eAAe,CAO3D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACxB,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,IAAI,GAChB,MAAM,IAAI,CAkBZ;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAChC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CASZ"}
@@ -1,6 +1,6 @@
1
- import type { SyncContext } from './context';
1
+ import type { SyncContext } from '../sync/context';
2
+ import type { ReindexReport } from '../sync/types';
2
3
  import { type HeldLease } from './lease';
3
- import type { ReindexReport } from './types';
4
4
  /**
5
5
  * A reindex, holding the sync's lease for as long as it runs: a follower in
6
6
  * another process would otherwise have what it just indexed removed, and
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reindex.d.ts","sourceRoot":"","sources":["../../src/run/reindex.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAO,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAKxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAgB,KAAK,SAAS,EAAwB,MAAM,SAAS,CAAC;AAiF7E;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAGtE;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAChC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,SAAS,GACd,OAAO,CAAC,aAAa,CAAC,CAexB"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../../src/sync/batch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAgBzC,iFAAiF;AACjF,wBAAsB,aAAa,CAClC,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,SAAS,GAAG,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,sDAAsD;AACtD,wBAAsB,SAAS,CAC9B,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,SAAS,OAAO,EAAE,GACrB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;GAGG;AACH,wBAAsB,IAAI,CACzB,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,SAAS,KAAK,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -1,7 +1,7 @@
1
1
  import type { AnyIndexDefinition, TypedIndex } from '@nxgt/meilisearch';
2
2
  import type { AnyCollectionDefinition, TypedCollection } from '@nxgt/mongo';
3
3
  import type { Collection } from 'mongodb';
4
- import type { LeaseDocument } from './lease';
4
+ import type { LeaseDocument } from '../run/lease';
5
5
  import type { SearchSyncOptions, SearchSyncState } from './types';
6
6
  /** Loosely typed: this layer works on any documents; the public types are what callers see. */
7
7
  export type Doc = Record<string, unknown>;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/sync/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAElE,+FAA+F;AAC/F,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC,uBAAuB,CAAC,CAAC;IAC9D,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,kBAAkB,CAAC,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IAC5C,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC;IAC3C,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,SAAS,GAAG,MAAM,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC;IAC/C,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CACxC;AAcD,wBAAgB,aAAa,CAC5B,OAAO,EAAE,iBAAiB,CAAC,uBAAuB,EAAE,kBAAkB,CAAC,GACrE,WAAW,CAqCb"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-search-sync.d.ts","sourceRoot":"","sources":["../../src/sync/create-search-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAK3D,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC/B,CAAC,SAAS,uBAAuB,EACjC,CAAC,SAAS,kBAAkB,EAC3B,OAAO,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAgB9C"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/sync/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGlD,4DAA4D;AAC5D,MAAM,MAAM,KAAK,GACd;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhD,sEAAsE;AACtE,eAAO,MAAM,KAAK,GAAI,IAAI,OAAO,WAAiC,CAAC;AAYnE;;;GAGG;AACH,wBAAsB,OAAO,CAC5B,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,GAAG,GAAG,SAAS,GACvB,OAAO,CAAC,KAAK,CAAC,CA6BhB"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/sync/errors.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,MAAM,mBAAmB;AAC9B,kEAAkE;AAChE,cAAc;AAChB,2EAA2E;GACzE,aAAa;AACf,0EAA0E;GACxE,gBAAgB;AAClB;;;GAGG;GACD,SAAS;AACX;;;GAGG;GACD,YAAY;AACd,0CAA0C;GACxC,QAAQ,CAAC;AAEZ,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CAC7B;AAED,mEAAmE;AACnE,qBAAa,eAAgB,SAAQ,KAAK;IAChC,IAAI,SAAqB;IAClC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,uBAAuB;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,EAAE,IAAI,GAAG,SAAS,CAAC;gBAEzB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB;CAU5D;AAED,4EAA4E;AAC5E,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,mBAWjE"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"running.d.ts","sourceRoot":"","sources":["../../src/sync/running.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7C;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAO/D"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/sync/start.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAI7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAQjD;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAiBxE"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/sync/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,2EAA2E;AAC3E,wBAAsB,SAAS,CAC9B,GAAG,EAAE,WAAW,GACd,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAMtC;AAED,+DAA+D;AAC/D,wBAAsB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhE;AAED,wEAAwE;AACxE,wBAAsB,SAAS,CAC9B,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,OAAO,GAChB,OAAO,CAAC,IAAI,CAAC,CAiBf"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Mongo } from '@nxgt/mongo';
2
+ import type { IndexMap, SearchSyncs, SearchSyncsConfig } from './types';
3
+ /**
4
+ * The search syncs: one `createSearchSync` per entry, over the collections
5
+ * the Mongo already holds.
6
+ *
7
+ * It is an object of its own rather than something added to the Mongo: the
8
+ * Mongo stays as `openMongo` returned it, and each of the two is closed by
9
+ * whoever opened it. The search syncs close only what they started — the
10
+ * collections, the client and the database are the Mongo's.
11
+ */
12
+ export declare function createSearchSyncs<C, const I extends IndexMap<I>>(mongo: Mongo<C>, config: SearchSyncsConfig<C, I>): SearchSyncs<I>;
13
+ //# sourceMappingURL=create-search-syncs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-search-syncs.d.ts","sourceRoot":"","sources":["../../src/syncs/create-search-syncs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEX,KAAK,EAEL,MAAM,aAAa,CAAC;AAQrB,OAAO,KAAK,EAEX,QAAQ,EAER,WAAW,EACX,iBAAiB,EACjB,MAAM,SAAS,CAAC;AA2CjB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAC/D,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,MAAM,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7B,WAAW,CAAC,CAAC,CAAC,CA4DhB"}
@@ -0,0 +1,89 @@
1
+ import type { AnyIndexDefinition, SyncOptions, SyncReport } from '@nxgt/meilisearch';
2
+ import type { AnyCollectionDefinition, CollectionsIn, CollectionsOf, DbName } from '@nxgt/mongo';
3
+ import type { ReindexReport, RunningSearchSync, SearchSync, SearchSyncOptions, SearchSyncState } from '../sync/types';
4
+ /**
5
+ * What a Mongo's sole database holds, by the name each definition is exported
6
+ * under — the same keys `mongo.db` answers to.
7
+ *
8
+ * A Mongo with several databases gives `never`, as `mongo.db` itself does:
9
+ * this version follows the collections of one. See the README.
10
+ */
11
+ export type SoleCollections<C> = DbName<C> extends infer N extends DbName<C> ? [Exclude<DbName<C>, N>] extends [never] ? CollectionsOf<CollectionsIn<C, N>> : never : never;
12
+ /**
13
+ * One collection's entry: everything `createSearchSync` takes except the
14
+ * collection itself, which the Mongo already holds.
15
+ */
16
+ export type SearchSyncEntry<Col extends AnyCollectionDefinition, I extends AnyIndexDefinition> = Omit<SearchSyncOptions<Col, I>, 'collection'>;
17
+ /** The index definitions a config names, one per key. */
18
+ export type IndexMap<I> = {
19
+ [K in keyof I]: AnyIndexDefinition;
20
+ };
21
+ /**
22
+ * The config `createSearchSyncs` takes: an entry per collection, under the key
23
+ * the Mongo wires that collection under.
24
+ *
25
+ * `I` is inferred from each entry's `index` alone, which is what lets a
26
+ * `transform` be written inline — its document is typed by the collection the
27
+ * key names, and its result by that index. A key the Mongo wires no collection
28
+ * for is refused by name.
29
+ */
30
+ export type SearchSyncsConfig<C, I extends IndexMap<I>> = {
31
+ [K in keyof I]: K extends keyof SoleCollections<C> ? SoleCollections<C>[K] extends infer Col extends AnyCollectionDefinition ? SearchSyncEntry<Col, I[K]> : never : {
32
+ index: `createSearchSyncs: this Mongo wires no collection called "${K & string}"`;
33
+ };
34
+ };
35
+ /** A report per key of the config, under the same names. */
36
+ export type ByKey<S, T> = {
37
+ readonly [K in keyof S]: T;
38
+ };
39
+ /**
40
+ * Every collection's sync, wired to the Mongo's collections — not started.
41
+ *
42
+ * `syncs` is each one as `@nxgt/mongo-meilisearch` builds it, so anything
43
+ * createSearchSyncs does not wrap is still reachable under its key.
44
+ */
45
+ export interface SearchSyncs<S> {
46
+ /** The syncs, under the keys the config used. */
47
+ readonly syncs: ByKey<S, SearchSync>;
48
+ /** Where each sync stands; `undefined` for one that never reindexed. */
49
+ state(): Promise<ByKey<S, SearchSyncState | undefined>>;
50
+ /**
51
+ * Brings every index the Mongo wires in line with its definition — created
52
+ * with its primary key, its settings updated where they differ — one
53
+ * after another, and reports each under its key. `@nxgt/meilisearch`'s
54
+ * `syncIndex`, per entry; the first that throws stops the rest. `dryRun`
55
+ * shows every settings difference, but a primary-key mismatch throws even
56
+ * then. A deployment step, run before `reindexAll` or `start`.
57
+ */
58
+ syncIndexes(options?: SyncOptions): Promise<ByKey<S, SyncReport>>;
59
+ /**
60
+ * Reindexes every collection, one after another, and reports each under
61
+ * its key. The first that throws stops the rest — a reindex removes what
62
+ * a collection no longer gives, so a half-finished run is not something
63
+ * to keep going from.
64
+ */
65
+ reindexAll(): Promise<ByKey<S, ReindexReport>>;
66
+ /**
67
+ * Starts every sync, and resolves once they are all hearing changes. One
68
+ * that fails closes the ones already started, so a failed `start` leaves
69
+ * nothing running.
70
+ */
71
+ start(): Promise<RunningSearchSyncs<S>>;
72
+ }
73
+ /** Every sync of a started `createSearchSyncs`. */
74
+ export interface RunningSearchSyncs<S> extends AsyncDisposable {
75
+ /** Each running sync, under the keys the config used. */
76
+ readonly running: ByKey<S, RunningSearchSync>;
77
+ /**
78
+ * Rejects with the first sync that stops on an error, and never resolves
79
+ * on its own — a sync that ends because `close` was called is not an
80
+ * event to wait for. **Await it or catch it**: an unhandled rejection
81
+ * ends the process.
82
+ */
83
+ readonly failed: Promise<never>;
84
+ /** Sends what every sync is holding, and records where each one is. */
85
+ flush(): Promise<void>;
86
+ /** Flushes, then stops every sync. Idempotent. */
87
+ close(): Promise<void>;
88
+ }
89
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/syncs/types.ts"],"names":[],"mappings":"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"}
package/docs/README.md CHANGED
@@ -5,7 +5,9 @@ example per area. These pages are the long one.
5
5
 
6
6
  | Page | Read it when |
7
7
  | --- | --- |
8
- | [The sync's lifecycle](guide/sync-lifecycle.md) | you are wiring a collection to an index for the first time, or looking for an option and its default |
8
+ | [The sync's lifecycle](guide/sync-lifecycle.md) | you are wiring one collection to an index for the first time, or looking for an option and its default |
9
+ | [Wiring it over a Mongo](guide/search-syncs/wiring.md) | one config has to follow several collections: an index and a transform each, under the keys `openMongo` wires, with `createSearchSyncs` |
10
+ | [The search syncs' lifecycle](guide/search-syncs/lifecycle.md) | you are reindexing, starting the syncs of several collections at once, watching for a failure, or shutting the process down |
9
11
  | [Reindexing](guide/reindex.md) | the index has to be filled, or refilled, from the collection |
10
12
  | [Following changes](guide/following-changes.md) | a process has to keep the index in step, and stop cleanly; several processes run the same sync and one must follow; or it stopped and you need to know why |
11
13
  | [What it leaves out](guide/boundaries.md) | you want to know what the lease on a sync name does not give, are about to join another collection in, or wonder who applies the index settings |