@kindgi/handler 0.1.1 → 0.1.3

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
@@ -14,7 +14,7 @@ code.
14
14
  | Symbol | Kind | What it is |
15
15
  | --- | --- | --- |
16
16
  | `NodeHandler` | type | `(input: unknown, ctx: NodeContext) => Promise<HandlerResult \| unknown>` |
17
- | `NodeContext` | type | What handlers receive: `runId`, `nodeId`, `tenantId`, `nodeOutputs`, `state`, `abortSignal`, `dryRun`, `principal?`, `authorize`, `can`, `check`, `clockNow`, `waitForToken` |
17
+ | `NodeContext` | type | What handlers receive: `runId`, `nodeId`, `tenantId`, `nodeOutputs`, `state`, `abortSignal`, `dryRun`, `principal?`, `authorize`, `can`, `check`, `clockNow`, `record`, `waitForToken` |
18
18
  | `HandlerResult` | type | Optional structured return — `{ output, stateDelta? }` |
19
19
  | `HandlerRegistry` | type | `ReadonlyMap<NodeId, NodeHandler>` |
20
20
  | `LoopContext` | type | Loop annotation (`loopNodeId`, `iteration`, enclosing-loop `path`) on the journal entries of nodes that ran inside a loop body |
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Action, Decision, Principal, ResourceRef } from '@kindgi/authz';
2
- import type { NodeId, RunId, TenantId } from '@kindgi/types';
2
+ import type { NodeId, OrgId, ProjectId, RunId, TenantId } from '@kindgi/types';
3
3
  /**
4
4
  * A node handler is the concrete code the runtime invokes when a flow
5
5
  * node becomes ready. The handler is entirely user-authored; the
@@ -50,6 +50,13 @@ export interface NodeContext {
50
50
  readonly nodeId: NodeId;
51
51
  /** The tenant this run belongs to — required for any tenant-scoped I/O. */
52
52
  readonly tenantId: TenantId;
53
+ /**
54
+ * The project this run belongs to, from the run (never from its input).
55
+ * Optional on the type: a test harness may build a context without one.
56
+ */
57
+ readonly projectId?: ProjectId;
58
+ /** That project's org, when it has one; resolved by the runtime from the project. */
59
+ readonly orgId?: OrgId;
53
60
  /**
54
61
  * Read-only outputs of previously-completed nodes, keyed by `NodeId`.
55
62
  * Present for fan-in and cross-branch reads.
@@ -102,8 +109,33 @@ export interface NodeContext {
102
109
  can(action: Action, resource: ResourceRef): Promise<boolean>;
103
110
  /** Full Decision — for advanced cases needing reason/evidence. */
104
111
  check(action: Action, resource: ResourceRef): Promise<Decision>;
105
- /** Read the clock. First call journals `Date.now()`; on replay, returns the journaled value. */
112
+ /**
113
+ * Read the clock, as `record` does: each call journals `Date.now()`.
114
+ * When the step resumes after a wait, its first call returns the time
115
+ * the first call read before, its second the second's, and so on; calls
116
+ * past those read the clock. A retry after a failure reads it afresh.
117
+ * **Determinism:** call it in the same order every time the step runs.
118
+ */
106
119
  clockNow(): Promise<number>;
120
+ /**
121
+ * Decide once for this step. The first call for `key` runs `decide`,
122
+ * journals its result, then returns it. When the step runs again
123
+ * (resumed after a wait, or retried after a failure), the call returns
124
+ * the journaled result and `decide` doesn't run, whatever the data it
125
+ * decided from says now.
126
+ *
127
+ * For a decision the step must keep across a wait: an approval gate's
128
+ * (did the policy ask for a review, under which token), so a policy
129
+ * changed meanwhile can't undo the park or lose the reviewer's answer.
130
+ *
131
+ * - The result is kept as JSON, and returned as JSON the first time too
132
+ * (a `Date` comes back as its string): `decide` returns JSON data.
133
+ * - `undefined` isn't journaled: the next call decides again.
134
+ * - `key` names the decision within the step; a step in a loop body
135
+ * keeps one per iteration. Calls for one key at the same time share
136
+ * one decision.
137
+ */
138
+ record<T>(key: string, decide: () => T | Promise<T>): Promise<T>;
107
139
  /**
108
140
  * Suspend the handler and wait for an external `completeToken(tokenId,
109
141
  * value)`. On first invocation: journals `wait.suspended`, inserts a
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE7D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,GAAG,OAAO,CAAC,CAAC;AAEjG;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACzD;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnD,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC;;;;;;;OAOG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B;;;;;;;;;OASG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,qEAAqE;IACrE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,kEAAkE;IAClE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE,gGAAgG;IAChG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAChF;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS;QAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvF;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,GAAG,OAAO;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;IAC7E,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kindgi/handler",
3
- "version": "0.1.1",
4
- "description": "Kindgi™ pack-author authoring surface. The types a pack author writes handlers against: NodeHandler (the function signature), NodeContext (what handlers receive at invocation — run scope, sibling outputs, waitForToken, authorize, clockNow, etc.), HandlerResult (structured return with optional stateDelta), HandlerRegistry (NodeId → NodeHandler map), LoopContext + LoopNodeOutput (loop-node shapes), plus WaitpointCancelledError (thrown by waitForToken when the wait is cancelled externally). Types plus that one error class — no other runtime code. Every runtime that executes Kindgi handlers implements this contract.",
3
+ "version": "0.1.3",
4
+ "description": "Kindgi™ pack-author authoring surface. The types a pack author writes handlers against: NodeHandler (the function signature), NodeContext (what handlers receive at invocation — run scope, sibling outputs, waitForToken, authorize, clockNow, record, etc.), HandlerResult (structured return with optional stateDelta), HandlerRegistry (NodeId → NodeHandler map), LoopContext + LoopNodeOutput (loop-node shapes), plus WaitpointCancelledError (thrown by waitForToken when the wait is cancelled externally). Types plus that one error class — no other runtime code. Every runtime that executes Kindgi handlers implements this contract.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -18,7 +18,8 @@
18
18
  "exports": {
19
19
  ".": {
20
20
  "types": "./dist/index.d.ts",
21
- "import": "./dist/index.js"
21
+ "import": "./dist/index.js",
22
+ "default": "./dist/index.js"
22
23
  }
23
24
  },
24
25
  "files": [
@@ -27,11 +28,11 @@
27
28
  "README.md"
28
29
  ],
29
30
  "dependencies": {
30
- "@kindgi/authz": "0.1.1",
31
- "@kindgi/types": "0.1.1"
31
+ "@kindgi/authz": "0.1.3",
32
+ "@kindgi/types": "0.1.3"
32
33
  },
33
34
  "engines": {
34
- "node": ">=22.0.0"
35
+ "node": ">=22.12.0"
35
36
  },
36
37
  "publishConfig": {
37
38
  "access": "public",
package/src/types.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // Copyright (C) 2026 Kindgi Inc.
3
3
 
4
4
  import type { Action, Decision, Principal, ResourceRef } from '@kindgi/authz';
5
- import type { NodeId, RunId, TenantId } from '@kindgi/types';
5
+ import type { NodeId, OrgId, ProjectId, RunId, TenantId } from '@kindgi/types';
6
6
 
7
7
  /**
8
8
  * A node handler is the concrete code the runtime invokes when a flow
@@ -57,6 +57,13 @@ export interface NodeContext {
57
57
  readonly nodeId: NodeId;
58
58
  /** The tenant this run belongs to — required for any tenant-scoped I/O. */
59
59
  readonly tenantId: TenantId;
60
+ /**
61
+ * The project this run belongs to, from the run (never from its input).
62
+ * Optional on the type: a test harness may build a context without one.
63
+ */
64
+ readonly projectId?: ProjectId;
65
+ /** That project's org, when it has one; resolved by the runtime from the project. */
66
+ readonly orgId?: OrgId;
60
67
  /**
61
68
  * Read-only outputs of previously-completed nodes, keyed by `NodeId`.
62
69
  * Present for fan-in and cross-branch reads.
@@ -109,8 +116,33 @@ export interface NodeContext {
109
116
  can(action: Action, resource: ResourceRef): Promise<boolean>;
110
117
  /** Full Decision — for advanced cases needing reason/evidence. */
111
118
  check(action: Action, resource: ResourceRef): Promise<Decision>;
112
- /** Read the clock. First call journals `Date.now()`; on replay, returns the journaled value. */
119
+ /**
120
+ * Read the clock, as `record` does: each call journals `Date.now()`.
121
+ * When the step resumes after a wait, its first call returns the time
122
+ * the first call read before, its second the second's, and so on; calls
123
+ * past those read the clock. A retry after a failure reads it afresh.
124
+ * **Determinism:** call it in the same order every time the step runs.
125
+ */
113
126
  clockNow(): Promise<number>;
127
+ /**
128
+ * Decide once for this step. The first call for `key` runs `decide`,
129
+ * journals its result, then returns it. When the step runs again
130
+ * (resumed after a wait, or retried after a failure), the call returns
131
+ * the journaled result and `decide` doesn't run, whatever the data it
132
+ * decided from says now.
133
+ *
134
+ * For a decision the step must keep across a wait: an approval gate's
135
+ * (did the policy ask for a review, under which token), so a policy
136
+ * changed meanwhile can't undo the park or lose the reviewer's answer.
137
+ *
138
+ * - The result is kept as JSON, and returned as JSON the first time too
139
+ * (a `Date` comes back as its string): `decide` returns JSON data.
140
+ * - `undefined` isn't journaled: the next call decides again.
141
+ * - `key` names the decision within the step; a step in a loop body
142
+ * keeps one per iteration. Calls for one key at the same time share
143
+ * one decision.
144
+ */
145
+ record<T>(key: string, decide: () => T | Promise<T>): Promise<T>;
114
146
  /**
115
147
  * Suspend the handler and wait for an external `completeToken(tokenId,
116
148
  * value)`. On first invocation: journals `wait.suspended`, inserts a