@toolproof-npm/shared 0.1.112 → 0.1.114

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.
@@ -2,7 +2,7 @@ import type { ResourceJson, ResourceRoleIdentityJson, ResourceRoleValueJson, Res
2
2
  import { CONSTANTS } from '../constants.js';
3
3
  export type BucketConst = typeof CONSTANTS.STORAGE.BUCKETS.tp_resources;
4
4
  export type CollectionConst = keyof typeof CONSTANTS.STORAGE.COLLECTIONS;
5
- export type TerminalConst = keyof typeof CONSTANTS.TERMINALS;
5
+ export type IDENTITY_PREFIX = keyof typeof CONSTANTS.IDENTITY_PREFIXES;
6
6
  export type StepConst = keyof typeof CONSTANTS.STEPS;
7
7
  export type Role = {
8
8
  identity: ResourceRoleIdentityJson;
@@ -1,26 +1,30 @@
1
1
  import { CONSTANTS } from '@toolproof-npm/shared/constants';
2
- import { getNewIdentity } from '@toolproof-npm/shared/server';
2
+ import { getNewIdentity, getNewStepIdentity, getExecutionRefFromWorkStepIdentity } from '@toolproof-npm/shared/server';
3
+ function assertNonEmpty(arr, msg) {
4
+ if (arr.length === 0)
5
+ throw new Error(msg);
6
+ }
3
7
  const mintNewRoleBindingMap = async (existingMap) => {
4
8
  const newMap = {};
5
9
  await Promise.all(Object.keys(existingMap).map(async (resourceRoleRef) => {
6
- const resourceId = await getNewIdentity(CONSTANTS.TERMINALS.resource);
10
+ const resourceId = await getNewIdentity(CONSTANTS.IDENTITY_PREFIXES.resource);
7
11
  newMap[resourceRoleRef] = resourceId;
8
12
  }));
9
13
  return newMap;
10
14
  };
11
15
  // DOC: Helper function to construct an Execution object from a job
12
16
  const createExecutionFromJob = async (job, executionRef) => {
13
- const execIdentity = executionRef ?? await getNewIdentity(CONSTANTS.TERMINALS.execution);
17
+ const execIdentity = executionRef ?? await getNewIdentity(CONSTANTS.IDENTITY_PREFIXES.execution);
14
18
  const inputBindingMap = {};
15
19
  const inputs = job.roles.inputMap;
16
20
  await Promise.all(Object.keys(inputs).map(async (resourceRoleRef) => {
17
- const resourceId = await getNewIdentity(CONSTANTS.TERMINALS.resource);
21
+ const resourceId = await getNewIdentity(CONSTANTS.IDENTITY_PREFIXES.resource);
18
22
  inputBindingMap[resourceRoleRef] = resourceId;
19
23
  }));
20
24
  const outputBindingMap = {};
21
25
  const outputs = job.roles.outputMap;
22
26
  await Promise.all(Object.keys(outputs).map(async (resourceRoleRef) => {
23
- const resourceId = await getNewIdentity(CONSTANTS.TERMINALS.resource);
27
+ const resourceId = await getNewIdentity(CONSTANTS.IDENTITY_PREFIXES.resource);
24
28
  outputBindingMap[resourceRoleRef] = resourceId;
25
29
  }));
26
30
  return {
@@ -34,8 +38,8 @@ const createExecutionFromJob = async (job, executionRef) => {
34
38
  };
35
39
  // DOC: Helper function to construct a WorkStep object from an jobMetaJson (does not append to statelessStrategy.steps)
36
40
  export const createWorkStepFromJob = async (job) => {
37
- const workStepIdentity = await getNewIdentity(CONSTANTS.STEPS.work);
38
- const executionRef = workStepIdentity.replace(CONSTANTS.STEPS.work.toUpperCase(), 'execution'.toUpperCase()); // ATTENTION: use function
41
+ const workStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.work);
42
+ const executionRef = getExecutionRefFromWorkStepIdentity(workStepIdentity);
39
43
  const execution = await createExecutionFromJob(job, executionRef);
40
44
  const newWorkStep = {
41
45
  identity: workStepIdentity,
@@ -52,8 +56,8 @@ export const createLoopStepFromJob = async (whatJob, whenJob, kind) => {
52
56
  const whenWorkStep = await createWorkStepFromJob(whenJob);
53
57
  // Generate loop step identity based on kind
54
58
  const stepIdentity = (kind === 'for'
55
- ? await getNewIdentity(CONSTANTS.STEPS.for)
56
- : await getNewIdentity(CONSTANTS.STEPS.while));
59
+ ? await getNewStepIdentity(CONSTANTS.STEPS.for)
60
+ : await getNewStepIdentity(CONSTANTS.STEPS.while));
57
61
  // Assemble the loop step
58
62
  return {
59
63
  identity: stepIdentity,
@@ -67,7 +71,7 @@ export const createLoopStepFromJob = async (whatJob, whenJob, kind) => {
67
71
  // DOC: Helper function to construct a BranchStep from an array of job pairs
68
72
  export const createBranchStepFromJobPairs = async (cases) => {
69
73
  // Generate branch step identity
70
- const branchStepIdentity = await getNewIdentity(CONSTANTS.STEPS.branch);
74
+ const branchStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.branch);
71
75
  // Create WorkSteps for each case (what and when pair)
72
76
  const casesWithWorkSteps = await Promise.all(cases.map(async ({ whatJob, whenJob }) => {
73
77
  const whatWorkStep = await createWorkStepFromJob(whatJob);
@@ -77,21 +81,25 @@ export const createBranchStepFromJobPairs = async (cases) => {
77
81
  when: whenWorkStep
78
82
  };
79
83
  }));
84
+ assertNonEmpty(casesWithWorkSteps, 'createBranchStepFromJobPairs requires at least one case');
85
+ const casesTuple = casesWithWorkSteps;
80
86
  // Assemble the branch step
87
+ const canonicalWhatExecutionRef = casesWithWorkSteps?.[0]?.what?.execution?.identity;
81
88
  return {
82
89
  identity: branchStepIdentity,
83
90
  kind: CONSTANTS.STEPS.branch,
84
- cases: casesWithWorkSteps
91
+ cases: casesTuple,
92
+ canonicalWhatExecutionRef: canonicalWhatExecutionRef ? String(canonicalWhatExecutionRef) : undefined
85
93
  };
86
94
  };
87
95
  // DOC: Helper function to clone a ForStep with new identities for steps and executions, preserving job references and bindings
88
96
  export const cloneForStep = async (forStep) => {
89
97
  // Generate new ForStep identity
90
- const newForStepIdentity = await getNewIdentity(CONSTANTS.STEPS.for);
98
+ const newForStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.for);
91
99
  // Clone the "what" WorkStep
92
100
  const originalWhatWorkStep = forStep.case.what;
93
- const newWhatWorkStepIdentity = await getNewIdentity(CONSTANTS.STEPS.work);
94
- const newWhatExecutionIdentity = newWhatWorkStepIdentity.replace(CONSTANTS.STEPS.work.toUpperCase(), 'execution'.toUpperCase());
101
+ const newWhatWorkStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.work);
102
+ const newWhatExecutionIdentity = getExecutionRefFromWorkStepIdentity(newWhatWorkStepIdentity);
95
103
  const newWhatExecution = {
96
104
  identity: newWhatExecutionIdentity,
97
105
  jobRef: originalWhatWorkStep.execution.jobRef,
@@ -107,8 +115,8 @@ export const cloneForStep = async (forStep) => {
107
115
  };
108
116
  // Clone the "when" WorkStep
109
117
  const originalWhenWorkStep = forStep.case.when;
110
- const newWhenWorkStepIdentity = await getNewIdentity(CONSTANTS.STEPS.work);
111
- const newWhenExecutionIdentity = newWhenWorkStepIdentity.replace(CONSTANTS.STEPS.work.toUpperCase(), 'execution'.toUpperCase());
118
+ const newWhenWorkStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.work);
119
+ const newWhenExecutionIdentity = getExecutionRefFromWorkStepIdentity(newWhenWorkStepIdentity);
112
120
  const newWhenExecution = {
113
121
  identity: newWhenExecutionIdentity,
114
122
  jobRef: originalWhenWorkStep.execution.jobRef,
@@ -135,11 +143,11 @@ export const cloneForStep = async (forStep) => {
135
143
  // DOC: Helper function to clone a WhileStep with new identities for steps and executions, preserving job references and bindings
136
144
  export const cloneWhileStep = async (whileStep) => {
137
145
  // Generate new WhileStep identity
138
- const newWhileStepIdentity = await getNewIdentity(CONSTANTS.STEPS.while);
146
+ const newWhileStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.while);
139
147
  // Clone the "what" WorkStep
140
148
  const originalWhatWorkStep = whileStep.case.what;
141
- const newWhatWorkStepIdentity = await getNewIdentity(CONSTANTS.STEPS.work);
142
- const newWhatExecutionIdentity = newWhatWorkStepIdentity.replace(CONSTANTS.STEPS.work.toUpperCase(), 'execution'.toUpperCase());
149
+ const newWhatWorkStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.work);
150
+ const newWhatExecutionIdentity = getExecutionRefFromWorkStepIdentity(newWhatWorkStepIdentity);
143
151
  const newWhatExecution = {
144
152
  identity: newWhatExecutionIdentity,
145
153
  jobRef: originalWhatWorkStep.execution.jobRef,
@@ -155,8 +163,8 @@ export const cloneWhileStep = async (whileStep) => {
155
163
  };
156
164
  // Clone the "when" WorkStep
157
165
  const originalWhenWorkStep = whileStep.case.when;
158
- const newWhenWorkStepIdentity = await getNewIdentity(CONSTANTS.STEPS.work);
159
- const newWhenExecutionIdentity = newWhenWorkStepIdentity.replace(CONSTANTS.STEPS.work.toUpperCase(), 'execution'.toUpperCase());
166
+ const newWhenWorkStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.work);
167
+ const newWhenExecutionIdentity = getExecutionRefFromWorkStepIdentity(newWhenWorkStepIdentity);
160
168
  const newWhenExecution = {
161
169
  identity: newWhenExecutionIdentity,
162
170
  jobRef: originalWhenWorkStep.execution.jobRef,
@@ -183,10 +191,20 @@ export const cloneWhileStep = async (whileStep) => {
183
191
  // DOC: Helper function to clone a BranchStep with a new identity, optionally overriding its cases.
184
192
  // Note: We intentionally do NOT clone executions/steps here because BranchStep (Option A) does not repeat cases.
185
193
  export const cloneBranchStep = async (branchStep, casesOverride) => {
186
- const newBranchStepIdentity = await getNewIdentity(CONSTANTS.STEPS.branch);
194
+ const newBranchStepIdentity = await getNewStepIdentity(CONSTANTS.STEPS.branch);
195
+ const cases = casesOverride ?? branchStep.cases;
196
+ const priorCanonical = branchStep.canonicalWhatExecutionRef;
197
+ const canonicalStillExists = priorCanonical
198
+ ? (cases ?? []).some((c) => String(c?.what?.execution?.identity ?? '') === String(priorCanonical))
199
+ : false;
200
+ const fallbackCanonical = (cases ?? [])?.[0]?.what?.execution?.identity;
201
+ const canonicalWhatExecutionRef = canonicalStillExists
202
+ ? priorCanonical
203
+ : (fallbackCanonical ? String(fallbackCanonical) : undefined);
187
204
  return {
188
205
  identity: newBranchStepIdentity,
189
206
  kind: CONSTANTS.STEPS.branch,
190
- cases: casesOverride ?? branchStep.cases,
207
+ cases,
208
+ canonicalWhatExecutionRef,
191
209
  };
192
210
  };
@@ -1,6 +1,6 @@
1
1
  import type { ExecutionJson, StatefulStrategyJson, StrategyRunJson, StrategyRunStatusJson, StrategyStateJson } from '@toolproof-npm/schema';
2
- import type { TerminalConst } from '../types.js';
3
- export type GetNewIdentity = (identifiable: TerminalConst) => string | Promise<string>;
2
+ import type { IDENTITY_PREFIX } from '../types.js';
3
+ export type GetNewIdentity = (identifiable: IDENTITY_PREFIX) => string | Promise<string>;
4
4
  export declare function normalizeExecutionInputBindingMap(execution: ExecutionJson, strategyState: StrategyStateJson): ExecutionJson;
5
5
  export declare function createStrategyRun(statefulStrategy: StatefulStrategyJson, opts?: {
6
6
  getNewIdentity: GetNewIdentity;
@@ -172,14 +172,14 @@ export async function createStrategyRun(statefulStrategy, opts) {
172
172
  // Normalize roleBindings.inputBindingMap to reflect the identities present in strategyState.
173
173
  // This is intentionally a pure transformation (no mutation of the provided statefulStrategy object graph).
174
174
  const normalizedStatefulStrategy = normalizeStatefulStrategyInputBindingMaps(statefulStrategy);
175
- const strategyRunIdentity = (await opts.getNewIdentity(CONSTANTS.TERMINALS.strategy_run));
175
+ const strategyRunIdentity = (await opts.getNewIdentity(CONSTANTS.IDENTITY_PREFIXES.strategy_run));
176
176
  const steps = normalizedStatefulStrategy.statelessStrategy?.steps ?? [];
177
177
  const strategyState = (normalizedStatefulStrategy.strategyState ?? {});
178
178
  // Algorithm to find what steps can run in parallel
179
179
  const threadStepsGroups = getIndependentThreads(steps, strategyState);
180
180
  const strategyThreadMap = {};
181
181
  for (const group of threadStepsGroups) {
182
- const threadIdentity = await opts.getNewIdentity(CONSTANTS.TERMINALS.strategy_thread);
182
+ const threadIdentity = await opts.getNewIdentity(CONSTANTS.IDENTITY_PREFIXES.strategy_thread);
183
183
  strategyThreadMap[threadIdentity] = group;
184
184
  }
185
185
  return {
@@ -15,7 +15,7 @@ export declare const CONSTANTS: {
15
15
  readonly members: "members";
16
16
  };
17
17
  };
18
- readonly TERMINALS: {
18
+ readonly IDENTITY_PREFIXES: {
19
19
  readonly format: "format";
20
20
  readonly type: "type";
21
21
  readonly role: "role";
@@ -33,6 +33,18 @@ export declare const CONSTANTS: {
33
33
  readonly while: "while";
34
34
  readonly for: "for";
35
35
  };
36
+ readonly STEP_IDENTITY_PREFIXES: {
37
+ readonly workstep: "workstep";
38
+ readonly branchstep: "branchstep";
39
+ readonly forstep: "forstep";
40
+ readonly whilestep: "whilestep";
41
+ };
42
+ readonly STEP_IDENTITY_PREFIX_BY_KIND: {
43
+ readonly work: "workstep";
44
+ readonly branch: "branchstep";
45
+ readonly for: "forstep";
46
+ readonly while: "whilestep";
47
+ };
36
48
  readonly ENGINE: {
37
49
  readonly GraphRunStrategy: "GraphRunStrategy";
38
50
  };
package/dist/constants.js CHANGED
@@ -15,7 +15,7 @@ export const CONSTANTS = {
15
15
  members: 'members',
16
16
  },
17
17
  },
18
- TERMINALS: {
18
+ IDENTITY_PREFIXES: {
19
19
  format: 'format',
20
20
  type: 'type',
21
21
  role: 'role',
@@ -33,6 +33,18 @@ export const CONSTANTS = {
33
33
  while: 'while',
34
34
  for: 'for',
35
35
  },
36
+ STEP_IDENTITY_PREFIXES: {
37
+ workstep: 'workstep',
38
+ branchstep: 'branchstep',
39
+ forstep: 'forstep',
40
+ whilestep: 'whilestep',
41
+ },
42
+ STEP_IDENTITY_PREFIX_BY_KIND: {
43
+ work: 'workstep',
44
+ branch: 'branchstep',
45
+ for: 'forstep',
46
+ while: 'whilestep',
47
+ },
36
48
  ENGINE: {
37
49
  GraphRunStrategy: 'GraphRunStrategy',
38
50
  },
@@ -1,5 +1,17 @@
1
- import type { ResourceTypeIdentityJson } from '@toolproof-npm/schema';
2
- import type { StepConst, ResourceMap, TerminalConst } from './_lib/types.js';
3
- export declare function getNewIdentity(identifiable: TerminalConst | StepConst): string;
1
+ import type { BranchStepIdentityJson, ExecutionIdentityJson, ForStepIdentityJson, IdentityByPrefix, ResourceTypeIdentityJson, WhileStepIdentityJson, WorkStepIdentityJson } from '@toolproof-npm/schema';
2
+ import type { ResourceMap, IDENTITY_PREFIX } from './_lib/types.js';
3
+ import { CONSTANTS } from './constants.js';
4
+ type StripTrailingS<S extends string> = S extends `${infer Rest}S` ? Rest : S;
5
+ type IdentityPrefixFor<T extends string> = StripTrailingS<Uppercase<T>>;
6
+ export type StepKind = keyof typeof CONSTANTS.STEPS;
7
+ export type StepIdentityPrefix = (typeof CONSTANTS.STEP_IDENTITY_PREFIX_BY_KIND)[StepKind];
8
+ type StepIdentityForPrefix<P extends StepIdentityPrefix> = P extends 'workstep' ? WorkStepIdentityJson : P extends 'branchstep' ? BranchStepIdentityJson : P extends 'forstep' ? ForStepIdentityJson : P extends 'whilestep' ? WhileStepIdentityJson : never;
9
+ type AnyIdentityPrefix = IDENTITY_PREFIX | StepIdentityPrefix;
10
+ export type NewIdentity<T extends AnyIdentityPrefix = AnyIdentityPrefix> = T extends keyof IdentityByPrefix ? IdentityByPrefix[T] : T extends StepIdentityPrefix ? StepIdentityForPrefix<T> : `${IdentityPrefixFor<T>}-${string}`;
11
+ export declare function getNewIdentity<T extends AnyIdentityPrefix>(identifiable: T): NewIdentity<T>;
12
+ export declare function getNewStepIdentityPrefix<K extends StepKind>(kind: K): (typeof CONSTANTS.STEP_IDENTITY_PREFIX_BY_KIND)[K];
13
+ export declare function getNewStepIdentity<K extends StepKind>(kind: K): Promise<StepIdentityForPrefix<(typeof CONSTANTS.STEP_IDENTITY_PREFIX_BY_KIND)[K]>>;
14
+ export declare function getExecutionRefFromWorkStepIdentity(workStepIdentity: WorkStepIdentityJson): ExecutionIdentityJson;
4
15
  export declare function listResources(// ATTENTION: must clean up
5
16
  resourceTypeRefs: ResourceTypeIdentityJson[]): Promise<ResourceMap>;
17
+ export {};
@@ -5,7 +5,17 @@ export function getNewIdentity(identifiable) {
5
5
  const normalized = base.endsWith('S') ? base.slice(0, -1) : base;
6
6
  const prefix = normalized + '-';
7
7
  const docRef = dbAdmin.collection(CONSTANTS.STORAGE.COLLECTIONS.resources).doc(identifiable).collection(CONSTANTS.STORAGE.COLLECTIONS.members).doc();
8
- return prefix + docRef.id;
8
+ return (prefix + docRef.id);
9
+ }
10
+ export function getNewStepIdentityPrefix(kind) {
11
+ return CONSTANTS.STEP_IDENTITY_PREFIX_BY_KIND[kind];
12
+ }
13
+ export async function getNewStepIdentity(kind) {
14
+ const prefix = getNewStepIdentityPrefix(kind);
15
+ return getNewIdentity(prefix);
16
+ }
17
+ export function getExecutionRefFromWorkStepIdentity(workStepIdentity) {
18
+ return workStepIdentity.replace('WORKSTEP', 'EXECUTION');
9
19
  }
10
20
  export async function listResources(// ATTENTION: must clean up
11
21
  resourceTypeRefs) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolproof-npm/shared",
3
- "version": "0.1.112",
3
+ "version": "0.1.114",
4
4
  "description": "Core library utilities for ToolProof",
5
5
  "keywords": [
6
6
  "toolproof",
@@ -56,7 +56,7 @@
56
56
  "typescript": "^5.9.3"
57
57
  },
58
58
  "dependencies": {
59
- "@toolproof-npm/schema": "^0.1.75",
59
+ "@toolproof-npm/schema": "^0.1.77",
60
60
  "firebase-admin": "^13.6.0"
61
61
  },
62
62
  "scripts": {