@ariestools/aries-dapp-core 0.1.8 → 0.1.10
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 +78 -5
- package/dist/bin/dappServer.mjs +79 -12
- package/dist/node/bin/dappServer.d.ts +8 -5
- package/dist/node/bin/dappServer.d.ts.map +1 -1
- package/dist/node/bin/loadReducer.d.ts +17 -0
- package/dist/node/bin/loadReducer.d.ts.map +1 -0
- package/dist/node/examples/productionComposition.d.ts +9 -5
- package/dist/node/examples/productionComposition.d.ts.map +1 -1
- package/examples/countFactsReducer.mjs +96 -0
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -152,12 +152,85 @@ published head).
|
|
|
152
152
|
## Local daemon (dev only)
|
|
153
153
|
|
|
154
154
|
The composed bin (`dist/bin/dappServer.mjs`) starts the storage server and a
|
|
155
|
-
|
|
155
|
+
DappActor. `aries dapp up` spawns it.
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
### CLI
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
# Empty spine (noop reducer)
|
|
161
|
+
aries dapp up
|
|
162
|
+
|
|
163
|
+
# Real project reducer (ESM .mjs or package export — TypeScript not supported)
|
|
164
|
+
aries dapp up --reducer ./dist/indexer.mjs
|
|
165
|
+
aries dapp up --reducer ./dist/indexer.mjs --reducer-export myIndex
|
|
166
|
+
aries dapp up --reducer @myorg/my-dapp/indexer
|
|
167
|
+
|
|
168
|
+
# Persist buckets across restarts
|
|
169
|
+
aries dapp up --backing disk --data-dir ~/.aries/dapp/data
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Example reducer shipped with this package:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
aries dapp up --reducer ./node_modules/@ariestools/aries-dapp-core/examples/countFactsReducer.mjs
|
|
176
|
+
# monorepo:
|
|
177
|
+
aries dapp up --reducer ./packages/dapp-core/examples/countFactsReducer.mjs
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Reducer module contract
|
|
181
|
+
|
|
182
|
+
The module must export a `DappReducer`:
|
|
183
|
+
|
|
184
|
+
- **default export**, or
|
|
185
|
+
- named export `reducer`, or
|
|
186
|
+
- any name via `--reducer-export` / `DAPP_REDUCER_EXPORT`
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
// dist/indexer.mjs
|
|
190
|
+
export default {
|
|
191
|
+
name: 'my-index',
|
|
192
|
+
version: '1',
|
|
193
|
+
async reduce({ data, state, index, signal, logger }) {
|
|
194
|
+
// read data, publishGeneration / publishIncremental, return progress
|
|
195
|
+
},
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Local publication should use `safety: { mode: 'unfenced' }` (or honor
|
|
200
|
+
`DAPP_PUBLICATION_SAFETY`, which the daemon sets to `unfenced` by default).
|
|
201
|
+
s3rver does not support reliable head CAS.
|
|
202
|
+
|
|
203
|
+
### Env (daemon)
|
|
204
|
+
|
|
205
|
+
| Variable | Default | Notes |
|
|
206
|
+
| --- | --- | --- |
|
|
207
|
+
| `DAPP_PORT` | `8801` | |
|
|
208
|
+
| `HOST` | `127.0.0.1` | |
|
|
209
|
+
| `DAPP_BACKING` | `memory` | `memory` \| `disk` |
|
|
210
|
+
| `DAPP_DATA_DIR` | `~/.aries/dapp/data` | disk root |
|
|
211
|
+
| `DAPP_REDUCER` | _(noop)_ | path to `.mjs`/`.js` or package export |
|
|
212
|
+
| `DAPP_REDUCER_EXPORT` | default / `reducer` | named export |
|
|
213
|
+
| `DAPP_REDUCE_INTERVAL_MS` | `5000` | |
|
|
214
|
+
| `DAPP_FIRST_RUN_DELAY_MS` | `0` | |
|
|
215
|
+
| `DAPP_PUBLICATION_SAFETY` | `unfenced` | set by daemon for project reducers |
|
|
216
|
+
| `DAPP_PUBLIC_HOST` / `DAPP_TLS_*` | | `--ssl auto` |
|
|
217
|
+
|
|
218
|
+
Under `--ssl auto` the daemon exports the local CA via `NODE_EXTRA_CA_CERTS`.
|
|
219
|
+
|
|
220
|
+
## Production entrypoint (not the CLI bin)
|
|
221
|
+
|
|
222
|
+
Do **not** run `aries-dapp-server` or `aries dapp up` in production. Write a
|
|
223
|
+
project-owned process that:
|
|
224
|
+
|
|
225
|
+
1. Builds an `S3Client` for R2/S3 (credentials from env/IAM — never logged)
|
|
226
|
+
2. Calls `createDappLocator({ client, bindings, ownClient: true })`
|
|
227
|
+
3. Implements a `DappReducer` (import statically — no dynamic CLI load required)
|
|
228
|
+
4. Calls `bootDappActors` with `safety: { mode: 'conditional-head' }` inside
|
|
229
|
+
`publishGeneration` / `publishIncremental`
|
|
230
|
+
5. Listens for SIGTERM and stops actors + `locator.destroy()`
|
|
231
|
+
|
|
232
|
+
See `examples/productionComposition.ts` and the composition section above.
|
|
233
|
+
There is no inbound public listener; clients read head/status/objects from CDN.
|
|
161
234
|
|
|
162
235
|
## Least-privilege IAM / R2 notes
|
|
163
236
|
|
package/dist/bin/dappServer.mjs
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
3
6
|
import { DAPP_BACKINGS, DAPP_BUCKETS, isDappBackingKind, resolveBacking, startDappServer } from "@ariestools/aries-dapp-serve";
|
|
4
7
|
import { AbstractCreatable, ConsoleLogger, IdLogger, assertEx, creatable } from "@ariestools/sdk";
|
|
5
8
|
import { DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
6
10
|
//#region src/actor/AbstractActor.ts
|
|
7
11
|
function createDeferred$1() {
|
|
8
12
|
let resolve;
|
|
@@ -837,16 +841,62 @@ const noopReducer = {
|
|
|
837
841
|
async reduce() {}
|
|
838
842
|
};
|
|
839
843
|
//#endregion
|
|
844
|
+
//#region src/bin/loadReducer.ts
|
|
845
|
+
const require = createRequire(import.meta.url);
|
|
846
|
+
function isPathSpec(spec) {
|
|
847
|
+
return spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("file:") || path.isAbsolute(spec) || spec.endsWith(".mjs") || spec.endsWith(".js") || spec.endsWith(".cjs");
|
|
848
|
+
}
|
|
849
|
+
function resolveFileUrl(spec) {
|
|
850
|
+
if (spec.startsWith("file:")) return spec;
|
|
851
|
+
const absolute = path.isAbsolute(spec) ? spec : path.resolve(process.cwd(), spec);
|
|
852
|
+
if (!existsSync(absolute)) throw new Error(`Reducer module not found: ${absolute}`);
|
|
853
|
+
return pathToFileURL(absolute).href;
|
|
854
|
+
}
|
|
855
|
+
function resolvePackageUrl(spec) {
|
|
856
|
+
try {
|
|
857
|
+
return pathToFileURL(require.resolve(spec, { paths: [process.cwd()] })).href;
|
|
858
|
+
} catch {
|
|
859
|
+
throw new Error(`Could not resolve reducer package "${spec}" from ${process.cwd()}. Pass a path to a .mjs file or an installed package export.`);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
function pickReducer(mod, exportName) {
|
|
863
|
+
if (exportName !== void 0 && exportName.length > 0) {
|
|
864
|
+
if (!(exportName in mod)) throw new Error(`Reducer module has no export named "${exportName}"`);
|
|
865
|
+
return mod[exportName];
|
|
866
|
+
}
|
|
867
|
+
if (mod.default !== void 0) return mod.default;
|
|
868
|
+
if (mod.reducer !== void 0) return mod.reducer;
|
|
869
|
+
throw new Error("Reducer module must default-export a DappReducer, or export `reducer`, or set DAPP_REDUCER_EXPORT / --reducer-export to a named export");
|
|
870
|
+
}
|
|
871
|
+
function assertReducer(value, label) {
|
|
872
|
+
if (value === null || typeof value !== "object") throw new TypeError(`${label} is not an object`);
|
|
873
|
+
const candidate = value;
|
|
874
|
+
if (typeof candidate.name !== "string" || candidate.name.length === 0) throw new TypeError(`${label} must have a non-empty string "name"`);
|
|
875
|
+
if (typeof candidate.reduce !== "function") throw new TypeError(`${label} must have a "reduce" function`);
|
|
876
|
+
return candidate;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Dynamically load a project-owned reducer for the local dapp daemon.
|
|
880
|
+
* Supports filesystem `.mjs`/`.js` paths and package export strings.
|
|
881
|
+
* TypeScript sources are not supported — build to ESM first.
|
|
882
|
+
*/
|
|
883
|
+
async function loadReducer(options) {
|
|
884
|
+
return assertReducer(pickReducer(await (isPathSpec(options.spec) ? import(resolveFileUrl(options.spec)) : import(resolvePackageUrl(options.spec))), options.exportName), `Reducer from ${options.spec}`);
|
|
885
|
+
}
|
|
886
|
+
//#endregion
|
|
840
887
|
//#region src/bin/dappServer.ts
|
|
841
888
|
/**
|
|
842
889
|
* **Local development only.** Composed dapp daemon: boots the embedded
|
|
843
890
|
* S3-compatible storage server (from `@ariestools/aries-dapp-serve`) and, in
|
|
844
|
-
* the same process, a shared process-wide locator plus
|
|
845
|
-
*
|
|
891
|
+
* the same process, a shared process-wide locator plus DappActor(s).
|
|
892
|
+
* Spawned by `aries dapp up`.
|
|
846
893
|
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
894
|
+
* Optional `DAPP_REDUCER` loads a project-owned ESM reducer (`.mjs` path or
|
|
895
|
+
* package export). Without it, `noopReducer` runs so the spine still works.
|
|
896
|
+
*
|
|
897
|
+
* Production indexers should **not** use this binary. Compose `dapp-core` from
|
|
898
|
+
* a project-owned entrypoint with real R2/S3 bindings and a real reducer —
|
|
899
|
+
* see the package README and `examples/`.
|
|
850
900
|
*/
|
|
851
901
|
const PORT = Number(process.env.DAPP_PORT ?? 8801);
|
|
852
902
|
const HOST = process.env.HOST ?? "127.0.0.1";
|
|
@@ -855,10 +905,22 @@ const PUBLIC_HOST = process.env.DAPP_PUBLIC_HOST;
|
|
|
855
905
|
const TLS_CERT_PATH = process.env.DAPP_TLS_CERT;
|
|
856
906
|
const TLS_KEY_PATH = process.env.DAPP_TLS_KEY;
|
|
857
907
|
const REDUCE_INTERVAL_MS = Number(process.env.DAPP_REDUCE_INTERVAL_MS ?? 5e3);
|
|
908
|
+
const FIRST_RUN_DELAY_MS = Number(process.env.DAPP_FIRST_RUN_DELAY_MS ?? 0);
|
|
909
|
+
const REDUCER_SPEC = process.env.DAPP_REDUCER;
|
|
910
|
+
const REDUCER_EXPORT = process.env.DAPP_REDUCER_EXPORT;
|
|
911
|
+
const DATA_DIR = process.env.DAPP_DATA_DIR ?? path.join(process.env.ARIES_HOME ?? path.join(homedir(), ".aries"), "dapp", "data");
|
|
912
|
+
async function resolveDaemonReducer() {
|
|
913
|
+
if (REDUCER_SPEC === void 0 || REDUCER_SPEC.length === 0) return noopReducer;
|
|
914
|
+
return await loadReducer({
|
|
915
|
+
spec: REDUCER_SPEC,
|
|
916
|
+
...REDUCER_EXPORT !== void 0 && REDUCER_EXPORT.length > 0 && { exportName: REDUCER_EXPORT }
|
|
917
|
+
});
|
|
918
|
+
}
|
|
858
919
|
async function main() {
|
|
859
920
|
if (!isDappBackingKind(BACKING)) throw new Error(`Unsupported DAPP_BACKING '${BACKING}' (supported: ${DAPP_BACKINGS.join(", ")})`);
|
|
860
921
|
if (TLS_CERT_PATH === void 0 !== (TLS_KEY_PATH === void 0)) throw new Error("DAPP_TLS_CERT and DAPP_TLS_KEY must be provided together");
|
|
861
|
-
const
|
|
922
|
+
const reducer = await resolveDaemonReducer();
|
|
923
|
+
const backing = resolveBacking(BACKING, { ...BACKING === "disk" && { directory: DATA_DIR } });
|
|
862
924
|
const server = await startDappServer({
|
|
863
925
|
backing,
|
|
864
926
|
host: HOST,
|
|
@@ -872,16 +934,21 @@ async function main() {
|
|
|
872
934
|
const logger = new ConsoleLogger();
|
|
873
935
|
const locator = createDappLocator({
|
|
874
936
|
endpoint: server.baseUrl,
|
|
875
|
-
logger
|
|
937
|
+
logger,
|
|
938
|
+
readOnlyData: false
|
|
876
939
|
});
|
|
940
|
+
process.env.DAPP_PUBLICATION_SAFETY ??= "unfenced";
|
|
941
|
+
process.env.DAPP_ENDPOINT ??= server.baseUrl;
|
|
877
942
|
const actors = await bootDappActors(locator, [{
|
|
878
943
|
name: "DappActor",
|
|
879
|
-
reducer
|
|
944
|
+
reducer,
|
|
880
945
|
reduceIntervalMs: REDUCE_INTERVAL_MS,
|
|
881
|
-
firstRunDelayMs:
|
|
882
|
-
publishStatus:
|
|
946
|
+
firstRunDelayMs: FIRST_RUN_DELAY_MS,
|
|
947
|
+
publishStatus: reducer.name !== "noop"
|
|
883
948
|
}]);
|
|
884
|
-
|
|
949
|
+
const versionSuffix = reducer.version === void 0 ? "" : `@${reducer.version}`;
|
|
950
|
+
const reducerLine = REDUCER_SPEC === void 0 ? "\nreducer: noop (pass DAPP_REDUCER / --reducer for real logic)" : `\nreducer: ${REDUCER_SPEC}`;
|
|
951
|
+
console.log(`[dev-only] dapp server listening at ${server.baseUrl}\n` + DAPP_BUCKETS.map((bucket) => ` /${bucket.padEnd(5)} ${server.baseUrl}/${bucket}`).join("\n") + `\nbacking: ${backing.description}\nactors: ${actors.length} (reducer '${reducer.name}'${versionSuffix}, every ${REDUCE_INTERVAL_MS}ms)` + reducerLine);
|
|
885
952
|
const shutdown = async () => {
|
|
886
953
|
for (const actor of actors) await actor.stop();
|
|
887
954
|
locator.destroy();
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* **Local development only.** Composed dapp daemon: boots the embedded
|
|
4
4
|
* S3-compatible storage server (from `@ariestools/aries-dapp-serve`) and, in
|
|
5
|
-
* the same process, a shared process-wide locator plus
|
|
6
|
-
*
|
|
5
|
+
* the same process, a shared process-wide locator plus DappActor(s).
|
|
6
|
+
* Spawned by `aries dapp up`.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Optional `DAPP_REDUCER` loads a project-owned ESM reducer (`.mjs` path or
|
|
9
|
+
* package export). Without it, `noopReducer` runs so the spine still works.
|
|
10
|
+
*
|
|
11
|
+
* Production indexers should **not** use this binary. Compose `dapp-core` from
|
|
12
|
+
* a project-owned entrypoint with real R2/S3 bindings and a real reducer —
|
|
13
|
+
* see the package README and `examples/`.
|
|
11
14
|
*/
|
|
12
15
|
export {};
|
|
13
16
|
//# sourceMappingURL=dappServer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dappServer.d.ts","sourceRoot":"","sources":["../../../src/bin/dappServer.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"dappServer.d.ts","sourceRoot":"","sources":["../../../src/bin/dappServer.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DappReducer } from '../reducer/DappReducer.ts';
|
|
2
|
+
export interface LoadReducerOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Named export to prefer when the module has no default export.
|
|
5
|
+
* Defaults to trying `reducer`, then `default`.
|
|
6
|
+
*/
|
|
7
|
+
exportName?: string;
|
|
8
|
+
/** Absolute or relative path to a `.mjs` file, or a resolvable package export. */
|
|
9
|
+
spec: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Dynamically load a project-owned reducer for the local dapp daemon.
|
|
13
|
+
* Supports filesystem `.mjs`/`.js` paths and package export strings.
|
|
14
|
+
* TypeScript sources are not supported — build to ESM first.
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadReducer(options: LoadReducerOptions): Promise<DappReducer>;
|
|
17
|
+
//# sourceMappingURL=loadReducer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadReducer.d.ts","sourceRoot":"","sources":["../../../src/bin/loadReducer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAI5D,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAA;CACb;AA6DD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAOnF"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Project-owned production composition example (documentation / type-check only).
|
|
2
|
+
* Project-owned **production** composition example (documentation / type-check only).
|
|
3
3
|
*
|
|
4
|
-
* This is **not**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* This is **not** the local `aries dapp up` / `aries-dapp-server` path.
|
|
5
|
+
* For local dev with a pluggable reducer, use:
|
|
6
|
+
*
|
|
7
|
+
* aries dapp up --reducer ./dist/indexer.mjs
|
|
8
|
+
*
|
|
9
|
+
* For production: copy this pattern into your application entrypoint. dapp-core
|
|
10
|
+
* stays application-neutral: you supply the reducer, store bindings, credentials,
|
|
11
|
+
* and any XL1/gateway readers. There is no inbound public listener and no
|
|
8
12
|
* application authority/signing seed stored in dapp-core.
|
|
9
13
|
*
|
|
10
14
|
* ```ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"productionComposition.d.ts","sourceRoot":"","sources":["../../../src/examples/productionComposition.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"productionComposition.d.ts","sourceRoot":"","sources":["../../../src/examples/productionComposition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmHG;AAEH,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example local-dev reducer for `aries dapp up --reducer`.
|
|
3
|
+
*
|
|
4
|
+
* Counts JSON objects under data/facts/, then publishes a coherent generation
|
|
5
|
+
* with the local unfenced safety mode (s3rver does not support head CAS).
|
|
6
|
+
*
|
|
7
|
+
* Usage (from a built monorepo):
|
|
8
|
+
*
|
|
9
|
+
* pnpm xy build @ariestools/aries-dapp-core
|
|
10
|
+
* aries dapp up --reducer ./packages/dapp-core/examples/countFactsReducer.mjs
|
|
11
|
+
*
|
|
12
|
+
* Seed data (path-style, any credentials):
|
|
13
|
+
*
|
|
14
|
+
* export S3_ENDPOINT=http://127.0.0.1:8801
|
|
15
|
+
* # put facts into the data bucket, e.g. with aws cli --endpoint-url
|
|
16
|
+
*
|
|
17
|
+
* Production: do **not** use this daemon. Compose dapp-core in your own
|
|
18
|
+
* process with R2 bindings and safety: { mode: 'conditional-head' }.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// Package-name import is intentional so this file is a drop-in template for
|
|
22
|
+
// app repos; monorepo eslint forbids absolute self-imports, so disable here.
|
|
23
|
+
// eslint-disable-next-line workspaces/no-absolute-imports -- example template
|
|
24
|
+
import {
|
|
25
|
+
publishGeneration,
|
|
26
|
+
readPublishedHead,
|
|
27
|
+
} from '@ariestools/aries-dapp-core'
|
|
28
|
+
|
|
29
|
+
/** @type {import('@ariestools/aries-dapp-core').DappReducer} */
|
|
30
|
+
export const reducer = {
|
|
31
|
+
name: 'count-facts',
|
|
32
|
+
version: '1',
|
|
33
|
+
async reduce({
|
|
34
|
+
data, state, index, signal, logger,
|
|
35
|
+
}) {
|
|
36
|
+
const keys = (await data.list('facts/')).sort()
|
|
37
|
+
const facts = []
|
|
38
|
+
for (const key of keys) {
|
|
39
|
+
const body = await data.get(key)
|
|
40
|
+
if (body === undefined) continue
|
|
41
|
+
facts.push(JSON.parse(new TextDecoder().decode(body)))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const prior = await readPublishedHead(state)
|
|
45
|
+
const safetyMode = process.env.DAPP_PUBLICATION_SAFETY === 'conditional-head'
|
|
46
|
+
? 'conditional-head'
|
|
47
|
+
: 'unfenced'
|
|
48
|
+
|
|
49
|
+
const published = await publishGeneration({
|
|
50
|
+
state,
|
|
51
|
+
index,
|
|
52
|
+
safety: { mode: safetyMode },
|
|
53
|
+
assertSafety: safetyMode === 'conditional-head',
|
|
54
|
+
...(prior?.headEtag !== undefined && { expectedHeadEtag: prior.headEtag }),
|
|
55
|
+
...(prior?.head.generation !== undefined
|
|
56
|
+
&& { previousGeneration: prior.head.generation }),
|
|
57
|
+
revision: (prior?.head.revision ?? 0) + 1,
|
|
58
|
+
reducer: { name: 'count-facts', version: '1' },
|
|
59
|
+
schemaVersion: '1',
|
|
60
|
+
source: {
|
|
61
|
+
cursor: String(facts.length),
|
|
62
|
+
completedPosition: String(facts.length),
|
|
63
|
+
observedHead: String(facts.length),
|
|
64
|
+
},
|
|
65
|
+
stateObjects: [{
|
|
66
|
+
key: 'view.json',
|
|
67
|
+
body: JSON.stringify({ count: facts.length, facts }),
|
|
68
|
+
contentType: 'application/json',
|
|
69
|
+
}],
|
|
70
|
+
indexObjects: [{
|
|
71
|
+
key: 'by-id.json',
|
|
72
|
+
body: JSON.stringify(Object.fromEntries(
|
|
73
|
+
facts.map(f => [String(f.id ?? ''), f]),
|
|
74
|
+
)),
|
|
75
|
+
contentType: 'application/json',
|
|
76
|
+
}],
|
|
77
|
+
signal,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
if (!published.ok) {
|
|
81
|
+
logger.warn(`publish conflict: ${published.conflict.kind} ${published.conflict.message}`)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
logger.info(`published generation ${published.generation} (count=${facts.length})`)
|
|
86
|
+
return {
|
|
87
|
+
cursor: String(facts.length),
|
|
88
|
+
lastCompletedPosition: String(facts.length),
|
|
89
|
+
observedSourceHead: String(facts.length),
|
|
90
|
+
generation: published.generation,
|
|
91
|
+
generationRoot: published.manifestKey,
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default reducer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ariestools/aries-dapp-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Aries dapp backend core: the DappActor (forked xl1 actor pattern), process-wide provider locator, pluggable reducer, and the composed daemon that runs the storage server plus actors",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ariestools",
|
|
@@ -35,17 +35,18 @@
|
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"dist",
|
|
38
|
+
"examples",
|
|
38
39
|
"README.md"
|
|
39
40
|
],
|
|
40
41
|
"dependencies": {
|
|
41
|
-
"@ariestools/sdk": "~8.1.
|
|
42
|
+
"@ariestools/sdk": "~8.1.3",
|
|
42
43
|
"@aws-sdk/client-s3": "~3.1090.0",
|
|
43
44
|
"tslib": "~2.8.1",
|
|
44
|
-
"@ariestools/aries-dapp-serve": "~0.1.
|
|
45
|
+
"@ariestools/aries-dapp-serve": "~0.1.10"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@ariestools/toolchain": "~8.7.
|
|
48
|
-
"@ariestools/tsconfig": "~8.7.
|
|
48
|
+
"@ariestools/toolchain": "~8.7.21",
|
|
49
|
+
"@ariestools/tsconfig": "~8.7.21",
|
|
49
50
|
"@opentelemetry/api": "~1.9.1",
|
|
50
51
|
"@types/node": "~26.1.1",
|
|
51
52
|
"rolldown": "~1.2.0",
|