@effectstream/runtime 0.100.14 → 0.100.15
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 +89 -0
- package/package.json +12 -12
- package/src/api/http-server.ts +22 -12
- package/src/main.ts +8 -1
- package/test/examples.test.ts +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# @effectstream/runtime
|
|
2
|
+
|
|
3
|
+
The state-machine runtime — the loop that ties sync, state machine,
|
|
4
|
+
database, events, and HTTP API together inside an EffectStream node.
|
|
5
|
+
Boot it with `init()` then drive it with `start(config)` and your node
|
|
6
|
+
is up.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
bun add @effectstream/runtime
|
|
12
|
+
# or
|
|
13
|
+
npm install @effectstream/runtime
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`@effectstream/runtime` is the canonical import for `init` and `start`,
|
|
17
|
+
used directly by every template. The same surface is also reachable
|
|
18
|
+
through `@effectstream/node-sdk/runtime` if you prefer the umbrella.
|
|
19
|
+
|
|
20
|
+
## Standalone usage
|
|
21
|
+
|
|
22
|
+
This package **owns the node's process model**. You don't pick parts of
|
|
23
|
+
it; you call `init()` once at boot and then `start(config)`. The config
|
|
24
|
+
brings together everything else:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { init, start } from "@effectstream/runtime";
|
|
28
|
+
import { Stm } from "@effectstream/sm";
|
|
29
|
+
import { config } from "./config.dev.ts";
|
|
30
|
+
|
|
31
|
+
await init();
|
|
32
|
+
|
|
33
|
+
const gameStm = new Stm(grammar);
|
|
34
|
+
gameStm.addStateTransition("join", function* () { /* ... */ });
|
|
35
|
+
|
|
36
|
+
await start({
|
|
37
|
+
config,
|
|
38
|
+
gameStateTransitions: [gameStm],
|
|
39
|
+
apiRouter: undefined, // optional Fastify route plugin
|
|
40
|
+
dbMigrations: [], // SQL migrations
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
While running, the runtime:
|
|
45
|
+
|
|
46
|
+
- Reads finalized blocks via `@effectstream/sync`.
|
|
47
|
+
- Routes batcher inputs through your registered `Stm`s.
|
|
48
|
+
- Commits all yielded SQL inside a per-block transaction.
|
|
49
|
+
- Publishes lifecycle and app events via `@effectstream/event-server`.
|
|
50
|
+
- Serves the optional Fastify API.
|
|
51
|
+
- Emits OpenTelemetry traces and logs through `@effectstream/log`.
|
|
52
|
+
|
|
53
|
+
## Inside EffectStream
|
|
54
|
+
|
|
55
|
+
`@effectstream/runtime` is the conductor: it doesn't define any chain
|
|
56
|
+
integrations or queries itself, but every other node package only gets
|
|
57
|
+
exercised when this loop is running. The `StartConfig` shape determines
|
|
58
|
+
which DB migrations apply, which state machines fire, and whether the
|
|
59
|
+
runtime exposes a Fastify router.
|
|
60
|
+
|
|
61
|
+
## Key exports
|
|
62
|
+
|
|
63
|
+
- `init()` — `Operation<void>`. One-shot setup: OpenTelemetry, config validation, version pinning. Call before `start`.
|
|
64
|
+
- `start(config: StartConfig)` — `Operation<void>`. Run the node loop until cancelled.
|
|
65
|
+
|
|
66
|
+
Types and helpers re-exported alongside `init` / `start`:
|
|
67
|
+
|
|
68
|
+
- `DBMigrations` — versioned SQL migrations passed into `start`.
|
|
69
|
+
- `StartConfig`, `StartConfigGameStateTransitions`, `StartConfigApiRouter` — `start`'s config types. Templates type-check against these implicitly but don't usually import them by name.
|
|
70
|
+
- `PrimitiveConstructor<T>` — extension point for new primitives.
|
|
71
|
+
- `VERSION` — `${number}.${number}.${number}` literal type for version pinning.
|
|
72
|
+
- Pagination helpers re-exported from `./api/pagination.ts`.
|
|
73
|
+
|
|
74
|
+
## Examples
|
|
75
|
+
|
|
76
|
+
The templates under
|
|
77
|
+
[`templates/`](https://github.com/PaimaStudios/paima-engine/tree/main/templates)
|
|
78
|
+
are full working `init()` + `start()` examples. The simplest is
|
|
79
|
+
[`templates/minimal/`](https://github.com/PaimaStudios/paima-engine/tree/main/templates/minimal).
|
|
80
|
+
|
|
81
|
+
End-to-end EVM sync test:
|
|
82
|
+
[`e2e/evm/sync/`](https://github.com/PaimaStudios/paima-engine/tree/main/e2e/evm/sync).
|
|
83
|
+
|
|
84
|
+
Runnable: [`test/examples.test.ts`](./test/examples.test.ts).
|
|
85
|
+
|
|
86
|
+
## Links
|
|
87
|
+
|
|
88
|
+
- Docs: https://effectstream.github.io/docs/packages/node/runtime
|
|
89
|
+
- Source: https://github.com/PaimaStudios/paima-engine/tree/main/packages/node-sdk/runtime
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effectstream/runtime",
|
|
3
|
-
"version": "0.100.
|
|
3
|
+
"version": "0.100.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://effectstream.github.io/docs/",
|
|
6
6
|
"repository": {
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
},
|
|
14
14
|
"exports": "./src/mod.ts",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@effectstream/concise": "0.100.
|
|
17
|
-
"@effectstream/config": "0.100.
|
|
18
|
-
"@effectstream/coroutine": "0.100.
|
|
19
|
-
"@effectstream/crypto": "0.100.
|
|
20
|
-
"@effectstream/db": "0.100.
|
|
21
|
-
"@effectstream/event-client": "0.100.
|
|
22
|
-
"@effectstream/event-server": "0.100.
|
|
23
|
-
"@effectstream/log": "0.100.
|
|
24
|
-
"@effectstream/sm": "0.100.
|
|
25
|
-
"@effectstream/sync": "0.100.
|
|
26
|
-
"@effectstream/utils": "0.100.
|
|
16
|
+
"@effectstream/concise": "0.100.15",
|
|
17
|
+
"@effectstream/config": "0.100.15",
|
|
18
|
+
"@effectstream/coroutine": "0.100.15",
|
|
19
|
+
"@effectstream/crypto": "0.100.15",
|
|
20
|
+
"@effectstream/db": "0.100.15",
|
|
21
|
+
"@effectstream/event-client": "0.100.15",
|
|
22
|
+
"@effectstream/event-server": "0.100.15",
|
|
23
|
+
"@effectstream/log": "0.100.15",
|
|
24
|
+
"@effectstream/sm": "0.100.15",
|
|
25
|
+
"@effectstream/sync": "0.100.15",
|
|
26
|
+
"@effectstream/utils": "0.100.15",
|
|
27
27
|
"@fastify/cors": "^11.0.1",
|
|
28
28
|
"@metamask/json-rpc-engine": "^10.0.3",
|
|
29
29
|
"pg": "8.17.2",
|
package/src/api/http-server.ts
CHANGED
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
type TypePaginationQuerySchema,
|
|
47
47
|
} from "./pagination.ts";
|
|
48
48
|
import { PrimitiveRegistry } from "@effectstream/sm";
|
|
49
|
-
import { ConfigNetworkType } from "@effectstream/config";
|
|
49
|
+
import { ConfigNetworkType, getWriteNamespace, usePaimaStaticConfig } from "@effectstream/config";
|
|
50
50
|
|
|
51
51
|
function tableListContains(
|
|
52
52
|
list: Array<{ table_name: string | null }>,
|
|
@@ -470,26 +470,36 @@ export const startHttpServer = function* (
|
|
|
470
470
|
return cleanedProtocols;
|
|
471
471
|
});
|
|
472
472
|
|
|
473
|
+
const staticConfigForRoute = yield* usePaimaStaticConfig();
|
|
474
|
+
const securityNamespaceForRoute = getWriteNamespace(
|
|
475
|
+
staticConfigForRoute.securityNamespace,
|
|
476
|
+
);
|
|
477
|
+
|
|
473
478
|
server.get("/config", {
|
|
474
479
|
schema: {
|
|
475
480
|
tags: ["developer"],
|
|
476
481
|
response: {
|
|
477
|
-
200: Type.
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
Type.Object({}, { additionalProperties: true }),
|
|
484
|
-
|
|
485
|
-
|
|
482
|
+
200: Type.Object({
|
|
483
|
+
securityNamespace: Type.Union([Type.String(), Type.Null()]),
|
|
484
|
+
syncProtocols: Type.Array(Type.Object({
|
|
485
|
+
networkType: Type.String(),
|
|
486
|
+
syncProtocolType: Type.String(),
|
|
487
|
+
syncProtocol: Type.Object({}, { additionalProperties: true }),
|
|
488
|
+
network: Type.Object({}, { additionalProperties: true }),
|
|
489
|
+
primitives: Type.Array(
|
|
490
|
+
Type.Object({}, { additionalProperties: true }),
|
|
491
|
+
),
|
|
492
|
+
}, { additionalProperties: true })),
|
|
493
|
+
}),
|
|
486
494
|
},
|
|
487
495
|
},
|
|
488
496
|
}, () => {
|
|
489
497
|
const config = syncProtocols.map((syncProtocol) => syncProtocol.config)
|
|
490
498
|
.flat();
|
|
491
|
-
|
|
492
|
-
|
|
499
|
+
return {
|
|
500
|
+
securityNamespace: securityNamespaceForRoute,
|
|
501
|
+
syncProtocols: clearBigInts(config),
|
|
502
|
+
};
|
|
493
503
|
});
|
|
494
504
|
}
|
|
495
505
|
|
package/src/main.ts
CHANGED
|
@@ -36,7 +36,8 @@ import type { Client } from "pg";
|
|
|
36
36
|
import type { EffectstreamBlockHash } from "@effectstream/utils";
|
|
37
37
|
import { applySystemMigrations } from "./version-migrations.ts";
|
|
38
38
|
import { getLastBlockHeight, getVersionInfo } from "@effectstream/db/version";
|
|
39
|
-
import {
|
|
39
|
+
import { ConfigNetworkType, usePaimaStaticConfig } from "@effectstream/config";
|
|
40
|
+
import type { SecurityNamespace, SyncProtocolWithNetwork } from "@effectstream/config";
|
|
40
41
|
import { builtInPrimitivesMap } from "@effectstream/sm";
|
|
41
42
|
import { validateAndSnapshotConfig } from "./config-snapshot.ts";
|
|
42
43
|
|
|
@@ -275,12 +276,16 @@ function* startup(
|
|
|
275
276
|
): Operation<AllSyncProtocols[]> {
|
|
276
277
|
const versionInfo = yield* getVersionInfo(dbConn);
|
|
277
278
|
const lastBlockHeight = yield* getLastBlockHeight(versionInfo, dbConn);
|
|
279
|
+
// Pull the security namespace from the static config so primitives that
|
|
280
|
+
// re-verify batched signatures can access it synchronously.
|
|
281
|
+
const staticConfig = yield* usePaimaStaticConfig();
|
|
278
282
|
// Create Runtime Primitives Instances
|
|
279
283
|
syncInfo.forEach((syncProtocol) => {
|
|
280
284
|
syncProtocol.primitives.forEach((primitive, primitiveIndex) => {
|
|
281
285
|
processPrimitives(
|
|
282
286
|
syncProtocol.primitives,
|
|
283
287
|
primitiveIndex,
|
|
288
|
+
staticConfig.securityNamespace,
|
|
284
289
|
config.userDefinedPrimitives
|
|
285
290
|
);
|
|
286
291
|
});
|
|
@@ -327,6 +332,7 @@ function* startup(
|
|
|
327
332
|
const processPrimitives = (
|
|
328
333
|
primitives: {primitive: any, id: string}[],
|
|
329
334
|
primitiveIndex: number,
|
|
335
|
+
securityNamespace: SecurityNamespace | undefined,
|
|
330
336
|
userDefinedPrimitives?: Record<string, any>,
|
|
331
337
|
) => {
|
|
332
338
|
const primitiveType = primitives[primitiveIndex].primitive.type;
|
|
@@ -349,6 +355,7 @@ const processPrimitives = (
|
|
|
349
355
|
const classConfig = {
|
|
350
356
|
...primitiveConfig,
|
|
351
357
|
instanceName: primitiveUniqueName,
|
|
358
|
+
securityNamespace,
|
|
352
359
|
}
|
|
353
360
|
if (isBuiltInPrimitive) {
|
|
354
361
|
p = new builtInPrimitivesMap[primitiveType as keyof typeof builtInPrimitivesMap](classConfig as any) ;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Examples for the README — verify the public surface resolves.
|
|
2
|
+
|
|
3
|
+
import { test, expect } from "bun:test";
|
|
4
|
+
import * as runtime from "../src/mod.ts";
|
|
5
|
+
|
|
6
|
+
test("README: init and start are exported as generator functions", () => {
|
|
7
|
+
expect(typeof runtime.init).toBe("function");
|
|
8
|
+
expect(typeof runtime.start).toBe("function");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("README: pagination helpers are re-exported", async () => {
|
|
12
|
+
const pagination = await import("../src/api/pagination.ts");
|
|
13
|
+
expect(Object.keys(pagination).length).toBeGreaterThan(0);
|
|
14
|
+
});
|