@pepys/ingest 0.1.1 → 0.3.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.
package/README.md CHANGED
@@ -22,6 +22,22 @@ delivery, and appends the facts it reads to the fact log.
22
22
  bun add @pepys/ingest
23
23
  ```
24
24
 
25
+ Serve a deployment's routes from an entry file, with the S2 settings and each secret its routes
26
+ name:
27
+
28
+ ```ts
29
+ // serve-ingest.ts
30
+ import { serveIngest } from '@pepys/ingest';
31
+ import { deployment } from './deployment.ts';
32
+
33
+ await serveIngest({ deployment, environment: process.env });
34
+ ```
35
+
36
+ ```sh
37
+ export PEPYS_S2_ACCESS_TOKEN # the S2 access token, set in your shell beforehand
38
+ PEPYS_S2_ENDPOINT=http://127.0.0.1:8090 PEPYS_S2_BASIN=pepys bun serve-ingest.ts
39
+ ```
40
+
25
41
  ## Features
26
42
 
27
43
  - `serveIngest` and `createIngest`: the receive routes of a deployment.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pepys/ingest",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "pepys ingest: the receive routes, the fact log and the blob store",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,8 +22,8 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@impz/client": "6.12.0",
25
- "@pepys/kit": "0.1.1",
26
- "@pepys/schema": "0.1.1",
25
+ "@pepys/kit": "0.3.0",
26
+ "@pepys/schema": "0.3.0",
27
27
  "@s2-dev/streamstore": "0.30.0",
28
28
  "@zgeoff/std": "0.4.3",
29
29
  "tiny-invariant": "1.3.3",
@@ -30,11 +30,12 @@ interface PullRunnerDependencies {
30
30
  append: BatchAppender;
31
31
  turns: StreamTurns;
32
32
  resyncMarks: ResyncMarks;
33
+ now: () => number;
33
34
  }
34
35
 
35
- // One run of one pull path: the pull, the envelope check, the appends, and then, for a resync pull,
36
- // the removal of every mark it was given. A pull's fact is noted in no mark: a resynced fact ties
37
- // with the body it replaces, and would mark its entity again on every run.
36
+ // For a resync pull, removes each mark it was given that no tie set again since. A pull's fact is
37
+ // noted in no mark: a resynced fact ties with the body it replaces, and would mark its entity again
38
+ // on every run.
38
39
  export function makePullRunner(dependencies: PullRunnerDependencies): PullRunner {
39
40
  return async (sourceName, pathName, cursor) => {
40
41
  const source = dependencies.deployment.sources.find(
@@ -54,7 +55,7 @@ export function makePullRunner(dependencies: PullRunnerDependencies): PullRunner
54
55
  env[name] = dependencies.secrets[name] ?? '';
55
56
  }
56
57
 
57
- const pulled = await path.pull(cursor, { env, marks });
58
+ const pulled = await path.pull(cursor, { env, marks, now: dependencies.now });
58
59
 
59
60
  if (pulled.kind !== 'batch') {
60
61
  return pulled;
@@ -85,7 +86,7 @@ export function makePullRunner(dependencies: PullRunnerDependencies): PullRunner
85
86
  }
86
87
 
87
88
  for (const mark of marks) {
88
- dependencies.resyncMarks.remove(source.name, mark.entity);
89
+ dependencies.resyncMarks.remove(source.name, mark);
89
90
  }
90
91
 
91
92
  return {
@@ -98,6 +98,7 @@ export function createIngest(options: IngestOptions): Ingest {
98
98
  append,
99
99
  turns,
100
100
  resyncMarks: resync.marks,
101
+ now: options.now,
101
102
  }),
102
103
  };
103
104
  }
@@ -8,7 +8,9 @@ import type { LastBody, StoredFactTime } from './collect-tied-entities.ts';
8
8
  // it. In memory only, so a restart loses them.
9
9
  export interface ResyncMarks {
10
10
  list: (source: string) => readonly ResyncMark[];
11
- remove: (source: string, entity: EntityID) => void;
11
+
12
+ // A mark set again while the pull ran is a new object, so it stays for the next pull.
13
+ remove: (source: string, mark: ResyncMark) => void;
12
14
  }
13
15
 
14
16
  // What the receive route of one source tells the marks.
@@ -20,8 +22,7 @@ export interface SourceMarks {
20
22
  interface SourceState {
21
23
  lastBodies: Map<EntityID, LastBody>;
22
24
 
23
- // each marked entity, with the locator of the last fact stored for it
24
- marks: Map<EntityID, string | undefined>;
25
+ marks: Map<EntityID, ResyncMark>;
25
26
  }
26
27
 
27
28
  export interface ResyncMarkStore {
@@ -53,10 +54,13 @@ export function createResyncMarks(options: ResyncMarksOptions): ResyncMarkStore
53
54
 
54
55
  return {
55
56
  marks: {
56
- list: (source) =>
57
- [...(sources.get(source)?.marks ?? [])].map(([entity, locator]) => ({ entity, locator })),
58
- remove: (source, entity) => {
59
- sources.get(source)?.marks.delete(entity);
57
+ list: (source) => [...(sources.get(source)?.marks.values() ?? [])],
58
+ remove: (source, mark) => {
59
+ const marks = sources.get(source)?.marks;
60
+
61
+ if (marks?.get(mark.entity) === mark) {
62
+ marks.delete(mark.entity);
63
+ }
60
64
  },
61
65
  },
62
66
  createSourceMarks: (source) => {
@@ -82,15 +86,19 @@ export function createResyncMarks(options: ResyncMarksOptions): ResyncMarkStore
82
86
  };
83
87
  }
84
88
 
85
- // The locator comes from the last body stored for the entity, which the caller has noted first.
89
+ // The locator and digest come from the last body stored for the entity, which the caller has noted
90
+ // first.
91
+ // Each call makes a new mark object, which `remove` tells apart from the one a pull was given.
86
92
  function addMarks(
87
93
  state: SourceState,
88
94
  entities: readonly EntityID[],
89
95
  options: ResyncMarksOptions,
90
96
  ): void {
91
97
  for (const entity of entities) {
98
+ const last = state.lastBodies.get(entity);
99
+
92
100
  state.marks.delete(entity);
93
- state.marks.set(entity, state.lastBodies.get(entity)?.locator);
101
+ state.marks.set(entity, { entity, locator: last?.locator, lastBodyDigest: last?.digest });
94
102
  }
95
103
 
96
104
  trimToCapacity(state.marks, options.capacity);
@@ -98,7 +106,7 @@ function addMarks(
98
106
 
99
107
  // drops the oldest entries first: a Map and a Set keep insertion order
100
108
  function trimToCapacity(
101
- entries: Map<EntityID, LastBody> | Map<EntityID, string | undefined>,
109
+ entries: Map<EntityID, LastBody> | Map<EntityID, ResyncMark>,
102
110
  capacity: number,
103
111
  ): void {
104
112
  for (const key of entries.keys()) {