@kairos-es/feed-algolia 0.0.0-stage → 0.0.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 (60) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +66 -2
  3. package/dist/cjs/classify.js +157 -0
  4. package/dist/cjs/classify.js.map +1 -0
  5. package/dist/cjs/identifiers.js +38 -0
  6. package/dist/cjs/identifiers.js.map +1 -0
  7. package/dist/cjs/index.js +39 -0
  8. package/dist/cjs/index.js.map +1 -0
  9. package/dist/cjs/records.js +105 -0
  10. package/dist/cjs/records.js.map +1 -0
  11. package/dist/cjs/rest.js +293 -0
  12. package/dist/cjs/rest.js.map +1 -0
  13. package/dist/cjs/sink.js +463 -0
  14. package/dist/cjs/sink.js.map +1 -0
  15. package/dist/dts-cjs/classify.d.ts +101 -0
  16. package/dist/dts-cjs/classify.d.ts.map +1 -0
  17. package/dist/dts-cjs/identifiers.d.ts +34 -0
  18. package/dist/dts-cjs/identifiers.d.ts.map +1 -0
  19. package/dist/dts-cjs/index.d.ts +39 -0
  20. package/dist/dts-cjs/index.d.ts.map +1 -0
  21. package/dist/dts-cjs/package.json +3 -0
  22. package/dist/dts-cjs/records.d.ts +56 -0
  23. package/dist/dts-cjs/records.d.ts.map +1 -0
  24. package/dist/dts-cjs/rest.d.ts +215 -0
  25. package/dist/dts-cjs/rest.d.ts.map +1 -0
  26. package/dist/dts-cjs/sink.d.ts +220 -0
  27. package/dist/dts-cjs/sink.d.ts.map +1 -0
  28. package/dist/dts-esm/classify.d.ts +101 -0
  29. package/dist/dts-esm/classify.d.ts.map +1 -0
  30. package/dist/dts-esm/identifiers.d.ts +34 -0
  31. package/dist/dts-esm/identifiers.d.ts.map +1 -0
  32. package/dist/dts-esm/index.d.ts +39 -0
  33. package/dist/dts-esm/index.d.ts.map +1 -0
  34. package/dist/dts-esm/package.json +3 -0
  35. package/dist/dts-esm/records.d.ts +56 -0
  36. package/dist/dts-esm/records.d.ts.map +1 -0
  37. package/dist/dts-esm/rest.d.ts +215 -0
  38. package/dist/dts-esm/rest.d.ts.map +1 -0
  39. package/dist/dts-esm/sink.d.ts +220 -0
  40. package/dist/dts-esm/sink.d.ts.map +1 -0
  41. package/dist/esm/classify.js +145 -0
  42. package/dist/esm/classify.js.map +1 -0
  43. package/dist/esm/identifiers.js +31 -0
  44. package/dist/esm/identifiers.js.map +1 -0
  45. package/dist/esm/index.js +38 -0
  46. package/dist/esm/index.js.map +1 -0
  47. package/dist/esm/package.json +4 -0
  48. package/dist/esm/records.js +96 -0
  49. package/dist/esm/records.js.map +1 -0
  50. package/dist/esm/rest.js +285 -0
  51. package/dist/esm/rest.js.map +1 -0
  52. package/dist/esm/sink.js +454 -0
  53. package/dist/esm/sink.js.map +1 -0
  54. package/package.json +33 -3
  55. package/src/classify.ts +201 -0
  56. package/src/identifiers.ts +45 -0
  57. package/src/index.ts +48 -0
  58. package/src/records.ts +109 -0
  59. package/src/rest.ts +773 -0
  60. package/src/sink.ts +733 -0
@@ -0,0 +1,454 @@
1
+ /**
2
+ * `makeAlgoliaSink` — a `FeedSink` onto one served Algolia index, with both optional
3
+ * parts: a `FeedResync` built on Algolia's index copy and move, and a
4
+ * `FeedReconcile` built on `browse`.
5
+ *
6
+ * ## The steady state
7
+ *
8
+ * `apply` is ONE `batch` to the served index (`records.ts` owns what goes in it and
9
+ * which records are refused before it is sent), and its receipt is the index and
10
+ * the task Algolia returned. `confirm` polls `getTask` on that index until the task
11
+ * is `published`, which is the moment Algolia serves the batch — the moment the
12
+ * port's `confirm` promises. The poll runs on an Effect `Schedule`: it backs off
13
+ * from `taskPolling.initialDelay`, doubling to `taskPolling.maxDelay`, and no poll
14
+ * STARTS after `taskPolling.timeout` has passed since the wait began — the last
15
+ * wait is cut short to land on that deadline rather than overrun it by up to
16
+ * `maxDelay`. A task still unpublished then is a `SinkRetryable`: the driver
17
+ * resends the batch, which is safe, rather than waiting on a task without bound.
18
+ * A poll that has started can still outlast the deadline by its own request
19
+ * timeout (`rest.ts`). A `Schedule` rather than a hand-written loop because the
20
+ * curve is then one declared value, run under the `Clock`, so a test's
21
+ * `TestClock` drives it like any other wait.
22
+ *
23
+ * ## Resync: a fresh index, swapped in whole
24
+ *
25
+ * Algolia's `operationIndex` move replaces the destination index atomically, which
26
+ * is exactly the swap `FeedResync` asks for. So:
27
+ *
28
+ * - `begin(generation)` names the target `<served>__kairos_g<generation>_<attempt>`,
29
+ * where `<attempt>` is drawn from Effect's `Random` afresh on every call, and
30
+ * then COPIES the served index into it with `scope` limited to settings, synonyms
31
+ * and rules. The fresh index then ranks and matches exactly as the served one does,
32
+ * and holds none of its records. A served index that has never been written has no
33
+ * scope to copy, and that is not an error: `begin` reads the served index's
34
+ * settings first and makes no copy when it does not exist. Algolia would copy a
35
+ * missing source as an empty index, but the copy's task is polled on the served
36
+ * index, and `getTask` on an index that does not exist answers 404, so skipping
37
+ * the copy is what keeps that wait off a missing index. The name is the ATTEMPT's, not the
38
+ * generation's, because two workers can be resyncing one generation at once —
39
+ * one of them having lost its lease without knowing it yet — and a target they
40
+ * shared is one either could publish half-built; `runFeedSync.ts` owns the
41
+ * argument. Random rather than the lease holder's id, because the port hands a
42
+ * sink no lease, and a draw needs none.
43
+ * - `begin` then MARKS the target as its attempt's own: it writes the target's
44
+ * `userData` setting as whatever the copy brought from the served index, plus one
45
+ * key, `kairosResyncAttempt`, holding the attempt. Algolia creates an index on a
46
+ * settings write, so this is also what makes the target EXIST whatever the copy
47
+ * did — over a served index never written, and for a rebuild that loads nothing.
48
+ * The mark is what `publish` checks, below. It goes in `userData` because that
49
+ * is the one setting Algolia keeps for the application's own data and reads
50
+ * nothing from, so the mark changes nothing about how the target ranks or
51
+ * matches.
52
+ * - `begin` first drops every OTHER resync index of this served index it can list,
53
+ * whatever its generation, settings snapshots included. Each was left by an attempt that will never publish
54
+ * it now — one that crashed, one abandoned because the generation moved, or one
55
+ * a worker that lost its lease is still loading — and every such index holds
56
+ * records against the application's quota; an ephemeral feed, whose generation
57
+ * is new on every boot, would otherwise leave one behind per interrupted boot.
58
+ * Dropping the one a stale worker is loading is also what makes that worker's
59
+ * `publish` fail rather than land. Listing needs the key to carry Algolia's
60
+ * `listIndexes` ACL, and the sink REQUIRES it rather than skipping the drop when
61
+ * it is missing: without the drop, a stale worker's `publish` could land and
62
+ * abandoned targets would pile up. A key without it is refused like any other
63
+ * 4xx, a `SinkHalted` at the start of the resync, before the copy, so nothing is
64
+ * created.
65
+ * - `load` is `apply`, against the target. It does not check the mark: that would
66
+ * cost a settings read per page and still race the drop, and `publish` is where
67
+ * a target is served, so `publish` is where the check has to hold.
68
+ * - `publish` reads the target's settings and refuses, `SinkRetryable`, a target
69
+ * that is missing or does not carry this attempt's mark; only then does it move
70
+ * the target over the served index and wait on the move's task. Then it reads the
71
+ * SERVED index's settings and reports success only if the served index now
72
+ * carries this attempt's mark, which only this move could have put there;
73
+ * otherwise it fails `SinkRetryable`. That read is the authority, and the
74
+ * move's own answer is not: Algolia ignores a move of a missing index "without
75
+ * returning an error", so a target dropped after the check gets a task back, the
76
+ * task publishes, and the served index still holds the old copy. Taking that for
77
+ * success would let the driver save its cursor at the new generation over an
78
+ * index that never got it. For the same
79
+ * reason a 404 on the move's task poll — `getTask` on an index that no longer
80
+ * exists — ends the wait rather than halting, and the read decides. An empty rebuild
81
+ * is a move like any other, of a target that holds no records, and it leaves the
82
+ * served index empty. A target can go missing before its publish in two ways: a
83
+ * newer `begin` dropped it, or something moved it over the served index first.
84
+ * Only the second could ever land a half-built target, and the per-attempt name
85
+ * is what rules it out: the one `publish` that names this target is this
86
+ * attempt's own, so no other worker's can move it. What is left is the first,
87
+ * and it has a second form, which is why the mark exists: a worker that lost its
88
+ * lease without knowing it goes on LOADING into its dropped target, and Algolia
89
+ * creates an index on a batch write, so the target comes back holding the stale
90
+ * worker's later pages alone. A name alone cannot tell that index from the one
91
+ * `begin` made; the missing mark can, since only `begin` writes it. Either way
92
+ * `publish` fails `SinkRetryable` and the resync starts again. It never touches
93
+ * the served index on that account, since the worker that finds its target gone
94
+ * is the one most likely to have lost the lease. An index a stale load brought
95
+ * back is left where it is, and the next `begin` drops it with the rest.
96
+ * - Before it marks the target, `begin` takes a SNAPSHOT of the served index's
97
+ * settings into `<target>__settings`, a settings-scoped copy, or an empty
98
+ * settings write when the served index does not exist. After the move has landed,
99
+ * `publish` takes the mark back off the served index by copying the snapshot's
100
+ * settings over it, scoped to settings, then drops the snapshot. The drop is a
101
+ * best effort of its own, and runs whether or not the restore landed. A copy is
102
+ * the one write that REMOVES a setting: Algolia stores a `null` written to `userData` as
103
+ * the value `null`, and `{}` or `resetToDefault` leave an empty object, so a
104
+ * rebuild of an index that had no `userData` would otherwise leave one behind.
105
+ * The snapshot is a second copy of the same served index's settings, made just
106
+ * after the target's, so the restore changes nothing else unless those settings
107
+ * changed between the two copies, when it applies the later ones. Its scope is
108
+ * SETTINGS ONLY, and must stay so: the snapshot holds no synonyms or rules, so a
109
+ * wider restore would wipe the served index's. Records are untouched, and so are the
110
+ * served index's replicas (see below). A mark an earlier failed restore left on the
111
+ * served index is taken off the snapshot as it is made, so it cannot come
112
+ * back; when it was all the `userData` there was, a settings write is the only
113
+ * way to take it off, and the `null` Algolia stores for it is what remains.
114
+ * The move and the restore are two steps, so a search index briefly carries the
115
+ * key, which nothing reads. The restore is BEST-EFFORT: the swap has
116
+ * already landed, and failing `publish` now would tell the driver it had not. A
117
+ * snapshot that is gone (a newer `begin` dropped it) is not copied, since a copy
118
+ * from a missing index changes nothing and its task never publishes; the mark
119
+ * then stays until the next rebuild's restore, and a warning says so. One that
120
+ * goes between that check and the copy is reported the same way, once the wait
121
+ * on the copy's task ends: at the first poll, if `getTask` on the missing index
122
+ * answers 404 as above, and at `pollTimeout` if it does not. The swap has landed
123
+ * by then, so the worst cost is a `publish` that returns late, not a wrong one.
124
+ *
125
+ * Check-then-move leaves no gap a stale load can use: the only worker that loads
126
+ * into this target is the one publishing it, and its loads have finished. A drop
127
+ * between the check and the move makes the move a no-op, which the read of the
128
+ * served index after it turns into a `SinkRetryable`, as above. Taking the mark
129
+ * off the TARGET before the move would open one: a drop between that write and
130
+ * the move, and the write itself would bring back an empty target for the move to
131
+ * land.
132
+ *
133
+ * Replicas are the one thing this does not settle by construction: a copy with
134
+ * `scope` is not documented to carry a primary's replicas to the target, nor a
135
+ * move over a primary, or the restore's settings-scoped copy over it, to keep
136
+ * them. Measured against a real application, all three leave them where they
137
+ * were, and the package's live test checks it after `publish` when credentials
138
+ * are present.
139
+ *
140
+ * ## Reconcile
141
+ *
142
+ * `listKeys` browses the served index page by page, retrieving `objectID` and nothing
143
+ * else, so listing a large index costs the ids and not the records.
144
+ *
145
+ * ## The key's ACLs
146
+ *
147
+ * A scoped key needs `addObject` (record writes, and the copy and the move),
148
+ * `deleteObject`, `deleteIndex` (the drop), `settings` (the mark check),
149
+ * `editSettings` (writing and removing the mark), `browse` (reconcile) and
150
+ * `listIndexes` (the drop's listing), on the served index and its resync indices.
151
+ * The README states the same set, ACL by ACL, for a consumer.
152
+ *
153
+ * ## What the sink holds
154
+ *
155
+ * Nothing between calls but its options and the captured `HttpClient`: no queue, no
156
+ * cursor, no buffered batch. Every piece of progress is the driver's, which is what
157
+ * lets a resent batch and a restarted resync be safe.
158
+ */
159
+ import { FeedKey, SinkHalted, SinkRetryable } from '@kairos-es/feed';
160
+ import { Clock, Duration, Effect, Option, Random, Schedule, Stream } from 'effect';
161
+ import { AlgoliaIndexName } from "./identifiers.js";
162
+ import { DEFAULT_MAX_RECORD_BYTES, encodeBatch } from "./records.js";
163
+ import { makeAlgoliaRest } from "./rest.js";
164
+ /**
165
+ * The resync target's name for one attempt at `generation`. PACKAGE-INTERNAL, and
166
+ * kept off the entry point deliberately: `attempt` is what `begin` draws for each
167
+ * call, so a real target's name is the one `begin` returns, and a consumer has no
168
+ * use for composing one.
169
+ *
170
+ * It does not check `attempt`. Every caller here passes lower-case base-36
171
+ * digits, which `begin` draws and the tests write by hand, and that shape is all
172
+ * `resyncGenerationOf` below recognises: a suffix of any other shape would name an
173
+ * index that `begin` never drops and `publish` never accepts.
174
+ */
175
+ export const resyncIndexName = (served, generation, attempt) => AlgoliaIndexName.make(`${served}__kairos_g${generation}_${attempt}`);
176
+ /**
177
+ * The generation `name` rebuilds, when it is a resync target of `served` for SOME
178
+ * generation and attempt, and `undefined` otherwise: the prefix, the decimal digits
179
+ * a `Generation` prints as, an underscore, then base-36 digits and nothing else,
180
+ * so a served index that merely starts with this one's name plus `__kairos_g` is not
181
+ * mistaken for one of its targets.
182
+ */
183
+ const resyncGenerationOf = (served, name) => {
184
+ const prefix = `${served}__kairos_g`;
185
+ if (!name.startsWith(prefix)) {
186
+ return undefined;
187
+ }
188
+ return /^(\d+)_[0-9a-z]+$/.exec(name.slice(prefix.length))?.[1];
189
+ };
190
+ /**
191
+ * The settings snapshot `begin` takes for `target`: the served index's settings as
192
+ * the target got them, before the mark. Named after its target, so it is a resync
193
+ * index of the same served index and `begin`'s drop of leftovers takes a snapshot an
194
+ * interrupted attempt left, as it takes the target.
195
+ */
196
+ const settingsSnapshotName = target => AlgoliaIndexName.make(`${target}__settings`);
197
+ /**
198
+ * Whether `name` is any resync index of `served`: a target, or a target's settings
199
+ * snapshot. What `begin` drops; `resyncGenerationOf` alone is what a target must be.
200
+ */
201
+ const isResyncIndexOf = (served, name) => resyncGenerationOf(served, name.replace(/__settings$/, '')) !== undefined;
202
+ /**
203
+ * The attempt a resync target's name ends in: the base-36 digits after its last
204
+ * underscore. Called only on a name `targetIndex` has already checked.
205
+ */
206
+ const attemptOf = name => /_([0-9a-z]+)$/.exec(name)?.[1] ?? '';
207
+ /** The `userData` key `begin` marks its target with; see the module doc. */
208
+ const ATTEMPT_MARK = 'kairosResyncAttempt';
209
+ /** Digits in a resync target's attempt suffix: 12 base-36 digits, about 62 bits. */
210
+ const ATTEMPT_DIGITS = 12;
211
+ /** One base-36 digit's worth of `Random`, `ATTEMPT_DIGITS` times. */
212
+ const drawAttempt = /*#__PURE__*/Effect.map(/*#__PURE__*/Effect.all(/*#__PURE__*/Array.from({
213
+ length: ATTEMPT_DIGITS
214
+ }, () => Random.nextIntBetween(0, 35))), digits => digits.map(d => d.toString(36)).join(''));
215
+ const RESYNC_SCOPE = ['settings', 'synonyms', 'rules'];
216
+ /** Index names per `listIndices` page; Algolia's own default. */
217
+ const LIST_PAGE_SIZE = 100;
218
+ const DEFAULT_POLL_INITIAL = /*#__PURE__*/Duration.millis(100);
219
+ const DEFAULT_POLL_MAX = /*#__PURE__*/Duration.seconds(2);
220
+ const DEFAULT_POLL_TIMEOUT = /*#__PURE__*/Duration.seconds(60);
221
+ const DEFAULT_BROWSE_PAGE = 1_000;
222
+ /** A positive duration from an option, or the default; dies on nonsense. */
223
+ const durationOption = (label, input, fallback) => {
224
+ if (input === undefined) {
225
+ return fallback;
226
+ }
227
+ const decoded = Duration.fromInputUnsafe(input);
228
+ if (!Duration.isFinite(decoded) || Duration.toMillis(decoded) <= 0) {
229
+ throw new Error(`feed-algolia: ${label} must be a positive, finite duration`);
230
+ }
231
+ return decoded;
232
+ };
233
+ const positiveInteger = (label, value, fallback) => {
234
+ if (value === undefined) {
235
+ return fallback;
236
+ }
237
+ if (!Number.isInteger(value) || value <= 0) {
238
+ throw new Error(`feed-algolia: ${label} must be a positive integer (got ${value})`);
239
+ }
240
+ return value;
241
+ };
242
+ /**
243
+ * An index's `userData` setting as an object to add a key to or take one from:
244
+ * empty when the index has none. A `userData` that is not a JSON object leaves no
245
+ * room for the mark beside it, and is a halt rather than something to overwrite.
246
+ */
247
+ const userDataOf = (index, settings) => {
248
+ const userData = Option.isSome(settings) ? settings.value.userData : undefined;
249
+ if (userData === undefined || userData === null) {
250
+ return Effect.succeed({});
251
+ }
252
+ return typeof userData === 'object' && !Array.isArray(userData) ? Effect.succeed(userData) : Effect.fail(new SinkHalted({
253
+ reason: `feed-algolia: the userData setting of '${index}' is not a JSON object, so a resync cannot mark its target beside it`
254
+ }));
255
+ };
256
+ /**
257
+ * The resync mark an index's settings carry, if any. Unlike {@link userDataOf} it
258
+ * never halts: after a move, a `userData` that is not an object only means the
259
+ * move did not land, which is the retryable case.
260
+ */
261
+ const attemptMarkOf = settings => {
262
+ const userData = Option.isSome(settings) ? settings.value.userData : undefined;
263
+ return typeof userData === 'object' && userData !== null && !Array.isArray(userData) ? userData[ATTEMPT_MARK] : undefined;
264
+ };
265
+ /**
266
+ * Narrow a call that sends no records. Only a write can be refused per record, so
267
+ * a `SinkRejected` from anything else is a bug in this package's classification;
268
+ * it is reported as a halt rather than silently dropped.
269
+ */
270
+ const noRejection = effect => Effect.mapError(effect, failure => failure._tag === 'SinkRejected' ? new SinkHalted({
271
+ reason: `feed-algolia: a call that sends no records was refused per record: ${failure.message}`
272
+ }) : failure);
273
+ /**
274
+ * Build an Algolia sink over the `HttpClient` in context; see the module doc.
275
+ * Dies at once on options out of range.
276
+ */
277
+ export const makeAlgoliaSink = options => Effect.gen(function* () {
278
+ const settings = yield* Effect.try({
279
+ try: () => ({
280
+ maxRecordBytes: positiveInteger('maxRecordBytes', options.maxRecordBytes, DEFAULT_MAX_RECORD_BYTES),
281
+ browsePageSize: positiveInteger('browsePageSize', options.browsePageSize, DEFAULT_BROWSE_PAGE),
282
+ pollInitial: durationOption('taskPolling.initialDelay', options.taskPolling?.initialDelay, DEFAULT_POLL_INITIAL),
283
+ pollMax: durationOption('taskPolling.maxDelay', options.taskPolling?.maxDelay, DEFAULT_POLL_MAX),
284
+ pollTimeout: durationOption('taskPolling.timeout', options.taskPolling?.timeout, DEFAULT_POLL_TIMEOUT)
285
+ }),
286
+ catch: error => error
287
+ }).pipe(Effect.orDie);
288
+ const rest = yield* makeAlgoliaRest(options);
289
+ const served = options.index;
290
+ const pollTimeoutMillis = Duration.toMillis(settings.pollTimeout);
291
+ /**
292
+ * The curve a task poll waits on between polls: doubling from the first delay,
293
+ * capped, and never past `deadline` — the last delay is cut to land on it, and
294
+ * nothing recurs once it is reached. Built per wait, since the deadline is
295
+ * the wait's own; the curve is the same for every one.
296
+ */
297
+ const pollSchedule = deadline => Schedule.min([Schedule.exponential(settings.pollInitial, 2), Schedule.spaced(settings.pollMax)]).pipe(Schedule.while(({
298
+ now
299
+ }) => now < deadline), Schedule.modifyDelay(({
300
+ now,
301
+ duration
302
+ }) => Effect.succeed(Duration.millis(Math.min(Duration.toMillis(duration), deadline - now)))));
303
+ /**
304
+ * Poll until `poll` is done; see the module doc for the curve. `poll` answers
305
+ * whether to stop, which for every wait but one is "the task is published".
306
+ */
307
+ const pollUntil = (index, taskID, poll) => Effect.gen(function* () {
308
+ const deadline = (yield* Clock.currentTimeMillis) + pollTimeoutMillis;
309
+ const done = yield* poll.pipe(Effect.repeat({
310
+ schedule: pollSchedule(deadline),
311
+ until: d => d
312
+ }));
313
+ if (!done) {
314
+ return yield* Effect.fail(new SinkRetryable({
315
+ reason: `Algolia task ${taskID} on index '${index}' was not published within ${Duration.format(settings.pollTimeout)}`
316
+ }));
317
+ }
318
+ });
319
+ /** Poll a task until it is published; see the module doc for the curve. */
320
+ const awaitTask = (index, taskID) => pollUntil(index, taskID, noRejection(rest.taskPublished(index, taskID)));
321
+ /** Wait on a task an index operation may or may not have returned. */
322
+ const awaitIfAny = (index, task) => Option.match(task, {
323
+ onNone: () => Effect.void,
324
+ onSome: taskID => awaitTask(index, taskID)
325
+ });
326
+ const send = (index, batch) => Effect.gen(function* () {
327
+ const encoded = yield* encodeBatch(options.doc, settings.maxRecordBytes, batch);
328
+ const taskID = yield* rest.batch(index, encoded.operations, encoded.keys);
329
+ return {
330
+ index,
331
+ taskID
332
+ };
333
+ });
334
+ /**
335
+ * The target's index name, refusing a target this sink did not begin: a
336
+ * target is named after the served index, its generation and its attempt, and a
337
+ * load into any other index would write somewhere no publish of this sink will
338
+ * move.
339
+ */
340
+ const targetIndex = target => resyncGenerationOf(served, target.name) === `${target.generation}` ? Effect.succeed(AlgoliaIndexName.make(target.name)) : Effect.die(new Error(`feed-algolia: the resync target '${target.name}' is not one of this sink's targets for generation ${target.generation} ('${served}__kairos_g${target.generation}_<attempt>')`));
341
+ /** Every name `listIndices` returns, page by page. */
342
+ const allIndexNames = Stream.paginate(0, page => Effect.map(noRejection(rest.listIndexNames(page, LIST_PAGE_SIZE)), answer => [answer.names, answer.more ? Option.some(page + 1) : Option.none()]));
343
+ /** Drop every resync target of `served` but `keep`; see the module doc. */
344
+ const dropAbandonedTargets = keep => Effect.gen(function* () {
345
+ const names = yield* Stream.runCollect(allIndexNames);
346
+ for (const name of names) {
347
+ if (name === keep || !isResyncIndexOf(served, name)) {
348
+ continue;
349
+ }
350
+ const index = AlgoliaIndexName.make(name);
351
+ yield* awaitIfAny(index, yield* noRejection(rest.deleteIndex(index)));
352
+ }
353
+ });
354
+ const resync = {
355
+ begin: generation => Effect.gen(function* () {
356
+ const attempt = yield* drawAttempt;
357
+ const target = resyncIndexName(served, generation, attempt);
358
+ yield* dropAbandonedTargets(target);
359
+ const snapshot = settingsSnapshotName(target);
360
+ // No copy of a served index that does not exist; see the module doc.
361
+ if (Option.isSome(yield* noRejection(rest.readSettings(served)))) {
362
+ yield* awaitTask(served, yield* noRejection(rest.operation(served, {
363
+ operation: 'copy',
364
+ destination: target,
365
+ scope: RESYNC_SCOPE
366
+ })));
367
+ yield* awaitTask(served, yield* noRejection(rest.operation(served, {
368
+ operation: 'copy',
369
+ destination: snapshot,
370
+ scope: ['settings']
371
+ })));
372
+ // A mark a failed restore left on the served index must not ride into
373
+ // the snapshot, or every restore would put it back.
374
+ const {
375
+ [ATTEMPT_MARK]: stale,
376
+ ...kept
377
+ } = yield* userDataOf(snapshot, yield* noRejection(rest.readSettings(snapshot)));
378
+ if (stale !== undefined) {
379
+ yield* awaitTask(snapshot, yield* noRejection(rest.writeSettings(snapshot, {
380
+ userData: Object.keys(kept).length === 0 ? null : kept
381
+ })));
382
+ }
383
+ } else {
384
+ // An index with every setting at its default, which is what the
385
+ // served index has; an empty settings write creates it.
386
+ yield* awaitTask(snapshot, yield* noRejection(rest.writeSettings(snapshot, {})));
387
+ }
388
+ const userData = yield* userDataOf(target, yield* noRejection(rest.readSettings(target)));
389
+ yield* awaitTask(target, yield* noRejection(rest.writeSettings(target, {
390
+ userData: {
391
+ ...userData,
392
+ [ATTEMPT_MARK]: attempt
393
+ }
394
+ })));
395
+ return {
396
+ generation,
397
+ name: target
398
+ };
399
+ }),
400
+ load: (target, batch) => Effect.flatMap(targetIndex(target), index => send(index, batch)),
401
+ publish: target => Effect.gen(function* () {
402
+ const index = yield* targetIndex(target);
403
+ const found = yield* noRejection(rest.readSettings(index));
404
+ const userData = yield* userDataOf(index, found);
405
+ if (userData[ATTEMPT_MARK] !== attemptOf(index)) {
406
+ return yield* Effect.fail(new SinkRetryable({
407
+ reason: Option.isNone(found) ? `the resync index '${index}' vanished before it could be moved over '${served}'` : `the resync index '${index}' is not the one its begin made (a load after a newer begin dropped it brought it back), so it is not moved over '${served}'`
408
+ }));
409
+ }
410
+ const moved = yield* noRejection(rest.operation(index, {
411
+ operation: 'move',
412
+ destination: served
413
+ }));
414
+ // A 404 on the poll ends the wait; the read below decides.
415
+ yield* pollUntil(index, moved, Effect.map(noRejection(rest.taskStateUnlessIndexMissing(index, moved)), state => state !== 'pending'));
416
+ const landed = yield* noRejection(rest.readSettings(served));
417
+ if (attemptMarkOf(landed) !== attemptOf(index)) {
418
+ return yield* Effect.fail(new SinkRetryable({
419
+ reason: `the move of the resync index '${index}' over '${served}' did not land: '${served}' does not carry its mark, so the index was dropped after publish checked it, or another attempt's move landed over it`
420
+ }));
421
+ }
422
+ // Best-effort, after the swap has landed; see the module doc.
423
+ const snapshot = settingsSnapshotName(index);
424
+ yield* Effect.gen(function* () {
425
+ // A copy from a missing snapshot changes nothing, and its task never
426
+ // publishes, so a snapshot a newer `begin` dropped is not waited on,
427
+ // unless it goes between this read and the copy; see the module doc.
428
+ if (Option.isNone(yield* noRejection(rest.readSettings(snapshot)))) {
429
+ return yield* Effect.fail(new SinkRetryable({
430
+ reason: `its settings snapshot '${snapshot}' is gone`
431
+ }));
432
+ }
433
+ yield* awaitTask(snapshot, yield* noRejection(rest.operation(snapshot, {
434
+ operation: 'copy',
435
+ destination: served,
436
+ scope: ['settings']
437
+ })));
438
+ }).pipe(Effect.catch(failure => Effect.logWarning(`feed-algolia: the resync mark was left in the userData of '${served}', and the next rebuild will take it off: ${failure.reason}`)));
439
+ // Separately, so a failed drop is not reported as a failed restore.
440
+ yield* Effect.flatMap(noRejection(rest.deleteIndex(snapshot)), task => awaitIfAny(snapshot, task)).pipe(Effect.catch(failure => Effect.logWarning(`feed-algolia: the settings snapshot '${snapshot}' was left behind, and the next rebuild will drop it: ${failure.reason}`)));
441
+ })
442
+ };
443
+ const reconcile = {
444
+ listKeys: Stream.paginate(Option.none(), cursor => Effect.map(noRejection(rest.browseObjectIds(served, cursor, settings.browsePageSize)), page => [page.objectIDs.filter(id => id.length > 0).map(id => FeedKey.make(id)), Option.map(page.cursor, Option.some)]))
445
+ };
446
+ return {
447
+ name: options.name,
448
+ apply: batch => send(served, batch),
449
+ confirm: receipt => awaitTask(receipt.index, receipt.taskID),
450
+ resync,
451
+ reconcile
452
+ };
453
+ });
454
+ //# sourceMappingURL=sink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sink.js","names":["FeedKey","SinkHalted","SinkRetryable","Clock","Duration","Effect","Option","Random","Schedule","Stream","AlgoliaIndexName","DEFAULT_MAX_RECORD_BYTES","encodeBatch","makeAlgoliaRest","resyncIndexName","served","generation","attempt","make","resyncGenerationOf","name","prefix","startsWith","undefined","exec","slice","length","settingsSnapshotName","target","isResyncIndexOf","replace","attemptOf","ATTEMPT_MARK","ATTEMPT_DIGITS","drawAttempt","map","all","Array","from","nextIntBetween","digits","d","toString","join","RESYNC_SCOPE","LIST_PAGE_SIZE","DEFAULT_POLL_INITIAL","millis","DEFAULT_POLL_MAX","seconds","DEFAULT_POLL_TIMEOUT","DEFAULT_BROWSE_PAGE","durationOption","label","input","fallback","decoded","fromInputUnsafe","isFinite","toMillis","Error","positiveInteger","value","Number","isInteger","userDataOf","index","settings","userData","isSome","succeed","isArray","fail","reason","attemptMarkOf","noRejection","effect","mapError","failure","_tag","message","makeAlgoliaSink","options","gen","try","maxRecordBytes","browsePageSize","pollInitial","taskPolling","initialDelay","pollMax","maxDelay","pollTimeout","timeout","catch","error","pipe","orDie","rest","pollTimeoutMillis","pollSchedule","deadline","min","exponential","spaced","while","now","modifyDelay","duration","Math","pollUntil","taskID","poll","currentTimeMillis","done","repeat","schedule","until","format","awaitTask","taskPublished","awaitIfAny","task","match","onNone","void","onSome","send","batch","encoded","doc","operations","keys","targetIndex","die","allIndexNames","paginate","page","listIndexNames","answer","names","more","some","none","dropAbandonedTargets","keep","runCollect","deleteIndex","resync","begin","snapshot","readSettings","operation","destination","scope","stale","kept","writeSettings","Object","load","flatMap","publish","found","isNone","moved","taskStateUnlessIndexMissing","state","landed","logWarning","reconcile","listKeys","cursor","browseObjectIds","objectIDs","filter","id","apply","confirm","receipt"],"sources":["../../src/sink.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,36 @@
1
1
  {
2
2
  "name": "@kairos-es/feed-algolia",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.0",
4
+ "description": "An Algolia sink for the kairos-es change feed, over effect's own HttpClient",
5
+ "license": "BSD-3-Clause",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/neverbland/kairos-es.git",
9
+ "directory": "packages/feed-algolia"
10
+ },
11
+ "sideEffects": [],
12
+ "homepage": "https://github.com/neverbland/kairos-es",
13
+ "peerDependencies": {
14
+ "effect": "^4.0.5",
15
+ "@kairos-es/feed": "^0.0.0"
16
+ },
17
+ "main": "./dist/cjs/index.js",
18
+ "module": "./dist/esm/index.js",
19
+ "types": "./dist/dts-cjs/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "import": {
23
+ "types": "./dist/dts-esm/index.d.ts",
24
+ "default": "./dist/esm/index.js"
25
+ },
26
+ "require": {
27
+ "types": "./dist/dts-cjs/index.d.ts",
28
+ "default": "./dist/cjs/index.js"
29
+ }
30
+ },
31
+ "./package.json": "./package.json"
32
+ },
33
+ "typesVersions": {
34
+ "*": {}
35
+ }
6
36
  }