@ariestools/aries-dapp-core 0.1.7 → 0.1.9
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 +326 -32
- package/dist/bin/dappServer.mjs +685 -104
- package/dist/node/DappActor.d.ts +28 -7
- package/dist/node/DappActor.d.ts.map +1 -1
- package/dist/node/actor/AbstractActor.d.ts +13 -8
- package/dist/node/actor/AbstractActor.d.ts.map +1 -1
- package/dist/node/actor/types.d.ts +7 -0
- package/dist/node/actor/types.d.ts.map +1 -1
- package/dist/node/bin/dappServer.d.ts +11 -4
- 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/boot/bootDappActors.d.ts +12 -1
- package/dist/node/boot/bootDappActors.d.ts.map +1 -1
- package/dist/node/consumer/api.d.ts +56 -0
- package/dist/node/consumer/api.d.ts.map +1 -0
- package/dist/node/consumer/hash.d.ts +6 -0
- package/dist/node/consumer/hash.d.ts.map +1 -0
- package/dist/node/consumer/parse.d.ts +18 -0
- package/dist/node/consumer/parse.d.ts.map +1 -0
- package/dist/node/consumer.d.ts +9 -0
- package/dist/node/consumer.d.ts.map +1 -0
- package/dist/node/consumer.mjs +330 -0
- package/dist/node/consumer.mjs.map +7 -0
- package/dist/node/examples/productionComposition.d.ts +118 -0
- package/dist/node/examples/productionComposition.d.ts.map +1 -0
- package/dist/node/index.d.ts +25 -3
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.mjs +1730 -68
- package/dist/node/index.mjs.map +4 -4
- package/dist/node/locator/createDappLocator.d.ts +32 -5
- package/dist/node/locator/createDappLocator.d.ts.map +1 -1
- package/dist/node/providers/DappBucketStore.d.ts +26 -21
- package/dist/node/providers/DappBucketStore.d.ts.map +1 -1
- package/dist/node/providers/capabilities.d.ts +49 -0
- package/dist/node/providers/capabilities.d.ts.map +1 -0
- package/dist/node/providers/errors.d.ts +11 -0
- package/dist/node/providers/errors.d.ts.map +1 -0
- package/dist/node/providers/hashBytes.d.ts +3 -0
- package/dist/node/providers/hashBytes.d.ts.map +1 -0
- package/dist/node/providers/joinKey.d.ts +11 -0
- package/dist/node/providers/joinKey.d.ts.map +1 -0
- package/dist/node/providers/memoryBucketStore.d.ts +14 -0
- package/dist/node/providers/memoryBucketStore.d.ts.map +1 -0
- package/dist/node/providers/types.d.ts +99 -0
- package/dist/node/providers/types.d.ts.map +1 -0
- package/dist/node/publication/canonicalHead.d.ts +7 -0
- package/dist/node/publication/canonicalHead.d.ts.map +1 -0
- package/dist/node/publication/commitHead.d.ts +9 -0
- package/dist/node/publication/commitHead.d.ts.map +1 -0
- package/dist/node/publication/constants.d.ts +27 -0
- package/dist/node/publication/constants.d.ts.map +1 -0
- package/dist/node/publication/gc.d.ts +42 -0
- package/dist/node/publication/gc.d.ts.map +1 -0
- package/dist/node/publication/incremental.d.ts +83 -0
- package/dist/node/publication/incremental.d.ts.map +1 -0
- package/dist/node/publication/publishGeneration.d.ts +18 -0
- package/dist/node/publication/publishGeneration.d.ts.map +1 -0
- package/dist/node/publication/putCreateOnly.d.ts +11 -0
- package/dist/node/publication/putCreateOnly.d.ts.map +1 -0
- package/dist/node/publication/releases.d.ts +49 -0
- package/dist/node/publication/releases.d.ts.map +1 -0
- package/dist/node/publication/safety.d.ts +55 -0
- package/dist/node/publication/safety.d.ts.map +1 -0
- package/dist/node/publication/shard.d.ts +7 -0
- package/dist/node/publication/shard.d.ts.map +1 -0
- package/dist/node/publication/status.d.ts +27 -0
- package/dist/node/publication/status.d.ts.map +1 -0
- package/dist/node/publication/statusPatch.d.ts +10 -0
- package/dist/node/publication/statusPatch.d.ts.map +1 -0
- package/dist/node/publication/types.d.ts +138 -0
- package/dist/node/publication/types.d.ts.map +1 -0
- package/dist/node/publication/writeGenerationObjects.d.ts +5 -0
- package/dist/node/publication/writeGenerationObjects.d.ts.map +1 -0
- package/dist/node/reducer/DappReducer.d.ts +37 -14
- package/dist/node/reducer/DappReducer.d.ts.map +1 -1
- package/examples/countFactsReducer.mjs +96 -0
- package/package.json +16 -8
package/README.md
CHANGED
|
@@ -1,38 +1,332 @@
|
|
|
1
1
|
# @ariestools/aries-dapp-core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Dapp backend core: a process-wide provider locator, pluggable reducer, coherent
|
|
4
|
+
generation publication for S3/R2-backed indexes, and the stock `DappActor`
|
|
5
|
+
timer. Designed for **outbound-only** production indexers (no public inbound
|
|
6
|
+
API on the compute process) as well as the local `aries dapp` development
|
|
7
|
+
fixture.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
## Development vs production
|
|
10
|
+
|
|
11
|
+
| Surface | Purpose |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `aries-dapp-server` bin + `noopReducer` | **Local development only** — embedded s3rver via `@ariestools/aries-dapp-serve`, memory backing, no real derivation |
|
|
14
|
+
| Project-owned entrypoint + real reducer | **Production** — inject an S3/R2 client, bind logical roles to buckets/prefixes, publish immutable generations |
|
|
15
|
+
|
|
16
|
+
`dapp-serve` and the noop daemon remain explicit local tools. Production usage
|
|
17
|
+
is a **project-owned** executable that composes this package; dapp-core does
|
|
18
|
+
not dynamically load arbitrary reducer modules and does not store application
|
|
19
|
+
authority (signing seeds, Webble controller roots, etc.).
|
|
20
|
+
|
|
21
|
+
Published state and indexes are **rebuildable projections**, not authority.
|
|
22
|
+
Public buckets must not be assumed safe for plaintext private material.
|
|
11
23
|
|
|
12
24
|
## Pieces
|
|
13
25
|
|
|
14
|
-
- **`AbstractActor`** —
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- **`
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- **`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- **`
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
- **`AbstractActor`** — create/start/stop lifecycle, non-overlapping timers
|
|
27
|
+
(`firstRunDelay` is the real delay before the first pass), abort signal on
|
|
28
|
+
stop, optional shutdown bound for in-flight work, readiness contract.
|
|
29
|
+
- **`DappProviderLocator` / `createDappLocator`** — process-wide locator. One
|
|
30
|
+
shared S3 client across roles, or **per-role clients** for least-privilege
|
|
31
|
+
principals; logical role → physical bucket + prefix + optional public CDN base.
|
|
32
|
+
The **data** role is registered read-only by default (`put` throws).
|
|
33
|
+
- **`DappObjectReader` / `DappObjectWriter` / `DappBucketStore`** — capability
|
|
34
|
+
split: reducers see `data` as read-only; state/index remain writers.
|
|
35
|
+
get/getWithMeta/stat/put (metadata + conditional writes)/list*/destroy.
|
|
36
|
+
- **`publishGeneration` / `readPublishedHead`** — fenced publication: create-only
|
|
37
|
+
generation objects, head-last with `conditional-head` CAS or injected lease
|
|
38
|
+
(or `unfenced` for local s3rver only). Returns `{ ok: true, … }` or
|
|
39
|
+
`{ ok: false, conflict }`.
|
|
40
|
+
- **`writeIndexerStatus` / `readIndexerStatus`** — durable floor/cursor,
|
|
41
|
+
completed position, observed source head, generation, success/error
|
|
42
|
+
timestamps, consecutive failures (CDN-revalidate).
|
|
43
|
+
- **`DappActor`** — drives a `DappReducer` on an interval; optional status
|
|
44
|
+
publication; optional `maxConsecutiveFailures` for supervisor restarts.
|
|
45
|
+
- **`bootDappActors`** — starts N actors, runs readiness, stops earlier actors
|
|
46
|
+
if a later one fails.
|
|
47
|
+
|
|
48
|
+
## Production composition (outbound-only)
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { S3Client } from '@aws-sdk/client-s3'
|
|
52
|
+
import {
|
|
53
|
+
bootDappActors,
|
|
54
|
+
createDappLocator,
|
|
55
|
+
publishGeneration,
|
|
56
|
+
type DappReducer,
|
|
57
|
+
} from '@ariestools/aries-dapp-core'
|
|
58
|
+
|
|
59
|
+
const client = new S3Client({
|
|
60
|
+
region: 'auto',
|
|
61
|
+
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
|
62
|
+
forcePathStyle: true,
|
|
63
|
+
credentials: {
|
|
64
|
+
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
|
|
65
|
+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const locator = createDappLocator({
|
|
70
|
+
client,
|
|
71
|
+
ownClient: true,
|
|
72
|
+
bindings: {
|
|
73
|
+
data: {
|
|
74
|
+
bucket: 'my-dapp',
|
|
75
|
+
prefix: 'data',
|
|
76
|
+
publicBaseUrl: 'https://cdn.example.com/data',
|
|
77
|
+
},
|
|
78
|
+
state: {
|
|
79
|
+
bucket: 'my-dapp',
|
|
80
|
+
prefix: 'state',
|
|
81
|
+
publicBaseUrl: 'https://cdn.example.com/state',
|
|
82
|
+
},
|
|
83
|
+
index: {
|
|
84
|
+
bucket: 'my-dapp',
|
|
85
|
+
prefix: 'index',
|
|
86
|
+
publicBaseUrl: 'https://cdn.example.com/index',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const reducer: DappReducer = {
|
|
92
|
+
name: 'my-app-indexer',
|
|
93
|
+
version: '1',
|
|
94
|
+
async reduce({ state, index, signal, logger }) {
|
|
95
|
+
const published = await publishGeneration({
|
|
96
|
+
state,
|
|
97
|
+
index,
|
|
98
|
+
safety: { mode: 'conditional-head' }, // fail closed if provider lacks CAS
|
|
99
|
+
// expectedHeadEtag: prior?.headEtag,
|
|
100
|
+
reducer: { name: 'my-app-indexer', version: '1' },
|
|
101
|
+
source: { cursor: '…', completedPosition: '…', observedHead: '…' },
|
|
102
|
+
stateObjects: [{ key: 'view.json', body: '{}', contentType: 'application/json' }],
|
|
103
|
+
indexObjects: [{ key: 'by-id.json', body: '{}', contentType: 'application/json' }],
|
|
104
|
+
signal,
|
|
105
|
+
})
|
|
106
|
+
if (!published.ok) {
|
|
107
|
+
logger.warn(`publish conflict: ${published.conflict.kind} ${published.conflict.message}`)
|
|
108
|
+
return // do not advance checkpoint on conflict
|
|
109
|
+
}
|
|
110
|
+
logger.info(`published ${published.generation}`)
|
|
111
|
+
return {
|
|
112
|
+
cursor: '…',
|
|
113
|
+
lastCompletedPosition: '…',
|
|
114
|
+
observedSourceHead: '…',
|
|
115
|
+
generation: published.generation,
|
|
116
|
+
generationRoot: published.manifestKey,
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const actors = await bootDappActors(locator, [{
|
|
122
|
+
name: 'MyIndexer',
|
|
123
|
+
reducer,
|
|
124
|
+
firstRunDelayMs: 1_000,
|
|
125
|
+
reduceIntervalMs: 15_000,
|
|
126
|
+
maxConsecutiveFailures: 10,
|
|
127
|
+
publishStatus: true,
|
|
128
|
+
}])
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Clients never contact this process. They read `head.json`, generation objects,
|
|
132
|
+
and `status.json` from the public CDN/base URLs. Credentials stay out of
|
|
133
|
+
published config and logs.
|
|
134
|
+
|
|
135
|
+
See also `src/examples/productionComposition.ts`.
|
|
136
|
+
|
|
137
|
+
### Publication layout
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
state/head.json # mutable head (revalidate CDN)
|
|
141
|
+
state/status.json # indexer status (revalidate CDN)
|
|
142
|
+
state/generations/<id>/manifest.json # immutable
|
|
143
|
+
state/generations/<id>/… # immutable state objects
|
|
144
|
+
index/generations/<id>/… # immutable index objects
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Head is written **last**. A crash before head leaves the previous complete
|
|
148
|
+
generation visible. Checkpoints must not advance past a fully published
|
|
149
|
+
generation (the actor only records generation after a successful reduce that
|
|
150
|
+
published head).
|
|
151
|
+
|
|
152
|
+
## Local daemon (dev only)
|
|
153
|
+
|
|
154
|
+
The composed bin (`dist/bin/dappServer.mjs`) starts the storage server and a
|
|
155
|
+
DappActor. `aries dapp up` spawns it.
|
|
156
|
+
|
|
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.
|
|
234
|
+
|
|
235
|
+
## Least-privilege IAM / R2 notes
|
|
236
|
+
|
|
237
|
+
- **Writer (private API):** state/index need `s3:GetObject`, `s3:PutObject`, and
|
|
238
|
+
usually `s3:ListBucket` on the role prefix. Data role: `GetObject`/`ListBucket`
|
|
239
|
+
only (no `PutObject`). Prefer distinct access keys via `createDappLocator({ clients: { data, state, index } })`.
|
|
240
|
+
- **Public CDN:** allow anonymous `GET`/`HEAD` only. **Deny** `s3:ListBucket` and
|
|
241
|
+
all `Put*`/`Delete*` for the public principal. dapp-core never requires public
|
|
242
|
+
LIST or PUT.
|
|
243
|
+
- **Credentials** must not appear in published head/manifest/status or logs.
|
|
244
|
+
|
|
245
|
+
## Publication safety
|
|
246
|
+
|
|
247
|
+
| Mode | Use |
|
|
248
|
+
| --- | --- |
|
|
249
|
+
| `conditional-head` | Production when the provider supports If-Match / If-None-Match |
|
|
250
|
+
| `lease` | Production with an application-injected lease/fencing adapter |
|
|
251
|
+
| `unfenced` | **Local s3rver / tests only** — no concurrent-writer safety |
|
|
252
|
+
|
|
253
|
+
## Releases, pins, rollback, GC
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
import {
|
|
257
|
+
listReleases, pinRelease, rollbackHead,
|
|
258
|
+
planGarbageCollection, runGarbageCollection,
|
|
259
|
+
} from '@ariestools/aries-dapp-core'
|
|
260
|
+
|
|
261
|
+
await pinRelease(state, 'gen-a', { reason: 'hotfix' })
|
|
262
|
+
await rollbackHead(state, {
|
|
263
|
+
targetGeneration: 'gen-a',
|
|
264
|
+
safety: { mode: 'conditional-head' },
|
|
265
|
+
expectedHeadEtag: currentEtag,
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
const plan = await planGarbageCollection({
|
|
269
|
+
state,
|
|
270
|
+
index,
|
|
271
|
+
policy: { mode: 'bounded', retainGenerations: 5, cdnGraceMs: 86_400_000 },
|
|
272
|
+
})
|
|
273
|
+
await runGarbageCollection(plan, { state, index }, { dryRun: true })
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Source **data** is never GC'd. Active head, pins, and in-flight generations are protected.
|
|
277
|
+
|
|
278
|
+
## Incremental high-cardinality publication
|
|
279
|
+
|
|
280
|
+
For large sparsely-changing indexes use `publishIncremental` (CAS bodies + 256-way
|
|
281
|
+
sharded map). Unchanged keys keep stable `cas/sha256/<hash>` URLs; each pass
|
|
282
|
+
writes only changed objects + touched shards + root + receipt.
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
import { publishIncremental } from '@ariestools/aries-dapp-core'
|
|
286
|
+
|
|
287
|
+
const result = await publishIncremental({
|
|
288
|
+
state,
|
|
289
|
+
index,
|
|
290
|
+
safety: { mode: 'conditional-head' },
|
|
291
|
+
expectedHeadEtag: prior?.headEtag,
|
|
292
|
+
reducer: { name: 'addr-index', version: '1' },
|
|
293
|
+
indexChanges: [{ key: 'by-source/0xabc', body: frameJson, contentType: 'application/json' }],
|
|
294
|
+
})
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Browser-neutral consumer
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import {
|
|
301
|
+
verifyPublishedView, resolveObject,
|
|
302
|
+
} from '@ariestools/aries-dapp-core/consumer'
|
|
303
|
+
|
|
304
|
+
const view = await verifyPublishedView({
|
|
305
|
+
fetch,
|
|
306
|
+
bases: { state: 'https://cdn.example/state', index: 'https://cdn.example/index' },
|
|
307
|
+
})
|
|
308
|
+
const one = await resolveObject({
|
|
309
|
+
fetch,
|
|
310
|
+
bases: { state: 'https://cdn.example/state', index: 'https://cdn.example/index' },
|
|
311
|
+
logicalKey: 'by-source/0xabc',
|
|
312
|
+
role: 'index',
|
|
313
|
+
})
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
No Node built-ins or AWS SDK on this subpath.
|
|
317
|
+
|
|
318
|
+
## Opt-in R2 contract test
|
|
319
|
+
|
|
320
|
+
Ordinary CI does not require R2 secrets. With credentials:
|
|
321
|
+
|
|
322
|
+
```sh
|
|
323
|
+
export DAPP_R2_CONTRACT_TEST=1
|
|
324
|
+
export R2_ACCOUNT_ID=…
|
|
325
|
+
export R2_ACCESS_KEY_ID=…
|
|
326
|
+
export R2_SECRET_ACCESS_KEY=…
|
|
327
|
+
export R2_BUCKET=…
|
|
328
|
+
# optional: R2_PUBLIC_BASE_URL=https://pub-….r2.dev
|
|
329
|
+
pnpm xy test @ariestools/aries-dapp-core
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
See `src/spec/r2.contract.spec.ts`.
|