@smthrs/flow 1.0.0-rc.12 → 1.0.0-rc.13

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.
@@ -13,8 +13,8 @@ npm install @smthrs/flow@next effect
13
13
 
14
14
  The `next` tag carries the 1.0 release candidates.
15
15
 
16
- `effect` is a peer dependency of this package, in the range `^4.0.4`. Add one
17
- `effect` in that range to your own dependencies: your declarations import
16
+ `effect` is a peer dependency of this package, at exactly `4.0.5`. Add one
17
+ `effect` at that version to your own dependencies: your declarations import
18
18
  `effect/Schema` and your implementations import `effect/Effect` directly, and two
19
19
  copies of `effect` in one program are two sets of service tags.
20
20
 
@@ -28,7 +28,7 @@ from, and the canonical JSON checks applied to values.
28
28
  ## Use the libraries
29
29
 
30
30
  Every `@smthrs` library installs from npm the same way. Name each package your
31
- code imports with the `next` tag, and add one `effect` in the `^4.0.4` range
31
+ code imports with the `next` tag, and add one `effect` in the `4.0.5` range
32
32
  the libraries peer. The [Quickstart](./quickstart.md) program needs these:
33
33
 
34
34
  ```bash
@@ -10,7 +10,7 @@ touches a database or the network, and the result arrives as a typed value
10
10
  decoded by the schema you declared.
11
11
 
12
12
  A runnable copy of this program is published in the Smithers examples,
13
- [`01-define-and-run.ts`](https://github.com/smithersai/smithers/blob/main/examples/src/01-define-and-run.ts).
13
+ [`01-define-and-run.ts`](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine/examples/01-define-and-run.ts).
14
14
 
15
15
  ## Prerequisites
16
16
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@smthrs/flow",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.12",
4
+ "version": "1.0.0-rc.13",
5
5
  "license": "MIT",
6
6
  "smthrs": {
7
7
  "group": "engine"
@@ -289,21 +289,21 @@
289
289
  "tag": "next"
290
290
  },
291
291
  "dependencies": {
292
- "@smthrs/crypto": "1.0.0-rc.12",
293
- "@smthrs/keys": "1.0.0-rc.12",
294
- "@smthrs/plan": "1.0.0-rc.12",
295
- "@smthrs/canonical": "1.0.0-rc.12",
296
- "@smthrs/capability": "1.0.0-rc.12"
292
+ "@smthrs/crypto": "1.0.0-rc.13",
293
+ "@smthrs/keys": "1.0.0-rc.13",
294
+ "@smthrs/plan": "1.0.0-rc.13",
295
+ "@smthrs/canonical": "1.0.0-rc.13",
296
+ "@smthrs/capability": "1.0.0-rc.13"
297
297
  },
298
298
  "devDependencies": {
299
299
  "@effect/language-service": "0.87.2",
300
- "@effect/platform-node": "4.0.4",
301
- "@effect/vitest": "4.0.4",
300
+ "@effect/platform-node": "4.0.5",
301
+ "@effect/vitest": "4.0.5",
302
302
  "@eslint/js": "9.39.5",
303
303
  "@types/node": "26.4.1",
304
304
  "@vitest/coverage-v8": "5.0.0",
305
305
  "dprint": "0.57.1",
306
- "effect": "4.0.4",
306
+ "effect": "4.0.5",
307
307
  "esbuild": "0.28.2",
308
308
  "eslint": "9.39.5",
309
309
  "eslint-import-resolver-typescript": "4.4.5",
@@ -316,7 +316,7 @@
316
316
  "vitest": "5.0.0"
317
317
  },
318
318
  "peerDependencies": {
319
- "effect": "^4.0.4"
319
+ "effect": "4.0.5"
320
320
  },
321
321
  "scripts": {
322
322
  "lint": "eslint src examples --max-warnings=0 && dprint check",
package/src/Flow/make.ts CHANGED
@@ -84,6 +84,20 @@ interface MakeOptions<
84
84
  * reach.
85
85
  */
86
86
  readonly modelInvocable?: boolean | undefined
87
+ /**
88
+ * The tokens, milliseconds and dollars one run asks a control plane to
89
+ * approve, and what exceeding them does, as a literal.
90
+ *
91
+ * Discovery reads it from the source text onto the descriptor's budget,
92
+ * exactly as Markdown frontmatter's `budget` is read; the control plane, not
93
+ * this flow, enforces it.
94
+ */
95
+ readonly budget?: {
96
+ readonly tokens?: number | undefined
97
+ readonly milliseconds?: number | undefined
98
+ readonly usd?: number | undefined
99
+ readonly onExceeded?: "fail" | "warn" | "skip-remaining" | "park" | undefined
100
+ } | undefined
87
101
  readonly idempotencyKey?:
88
102
  | ((
89
103
  payload: Payload extends Schema.Struct.Fields ? Schema.Struct.Type<Payload>
package/src/HumanTask.ts CHANGED
@@ -1004,4 +1004,18 @@ export const answer = (options: {
1004
1004
  export const decode =
1005
1005
  <S extends Schema.ConstraintDecoder<unknown>>(schema: S) =>
1006
1006
  <E, R>(self: Node.Node<typeof Schema.Json.Type, E, R>): Node.Node<S["Type"], E, R> =>
1007
- Node.map(self, Schema.decodeUnknownSync(schema))
1007
+ Node.map(self, answerDecoder(schema))
1008
+
1009
+ // Schema decoders are opaque callbacks. Reuse one per immutable schema so a
1010
+ // same-process rebuild preserves its identity without claiming portable
1011
+ // identity for arbitrary schema transformations or checks.
1012
+ const answerDecoders = new WeakMap<object, (input: unknown) => unknown>()
1013
+
1014
+ const answerDecoder = <S extends Schema.ConstraintDecoder<unknown>>(schema: S): (input: unknown) => S["Type"] => {
1015
+ let decoder = answerDecoders.get(schema)
1016
+ if (decoder === undefined) {
1017
+ decoder = Schema.decodeUnknownSync(schema)
1018
+ answerDecoders.set(schema, decoder)
1019
+ }
1020
+ return decoder
1021
+ }
@@ -1486,12 +1486,27 @@ export const layer = <
1486
1486
 
1487
1487
  // These layers only register handlers at construction. Crypto is supplied by
1488
1488
  // the executing interpreter (Services), not required to register a flow.
1489
- const systemActions = Layer.mergeAll(HumanTask.layer, Sleep.layer, WaitFor.layer) as Layer.Layer<
1489
+ const systemHandlers = Layer.mergeAll(HumanTask.layer, Sleep.layer, WaitFor.layer) as Layer.Layer<
1490
1490
  never,
1491
1491
  never,
1492
1492
  FlowRuntime
1493
1493
  >
1494
1494
 
1495
+ // Independent module/refresh layer builds can share one implementation table.
1496
+ // Memoize the built-ins by that owner, with Layer retaining each live scope's
1497
+ // reference, rather than registering a second handler or overriding the first.
1498
+ const systemMemoMaps = new WeakMap<Implementations["Service"], Layer.MemoMap>()
1499
+ const systemActions = Layer.fromBuild((currentMemoMap, scope) =>
1500
+ Effect.flatMap(Implementations, (table) => {
1501
+ let memoMap = systemMemoMaps.get(table)
1502
+ if (memoMap === undefined) {
1503
+ memoMap = currentMemoMap
1504
+ systemMemoMaps.set(table, memoMap)
1505
+ }
1506
+ return Layer.buildWithMemoMap(systemHandlers, memoMap, scope)
1507
+ })
1508
+ )
1509
+
1495
1510
  /** Registers the interpreter and reachable child flows under one policy. @private */
1496
1511
  const makeLayer = <
1497
1512
  Tag extends string,