@pgflow/dsl 0.0.5-prealpha.2
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/LICENSE.md +660 -0
- package/README.md +11 -0
- package/__tests__/runtime/flow.test.ts +121 -0
- package/__tests__/runtime/steps.test.ts +183 -0
- package/__tests__/runtime/utils.test.ts +149 -0
- package/__tests__/types/dsl-types.test-d.ts +103 -0
- package/__tests__/types/example-flow.test-d.ts +76 -0
- package/__tests__/types/extract-flow-input.test-d.ts +71 -0
- package/__tests__/types/extract-flow-steps.test-d.ts +74 -0
- package/__tests__/types/getStepDefinition.test-d.ts +65 -0
- package/__tests__/types/step-input.test-d.ts +212 -0
- package/__tests__/types/step-output.test-d.ts +55 -0
- package/brainstorming/condition/condition-alternatives.md +219 -0
- package/brainstorming/condition/condition-with-flexibility.md +303 -0
- package/brainstorming/condition/condition.md +139 -0
- package/brainstorming/condition/implementation-plan.md +372 -0
- package/brainstorming/dsl/cli-json-schema.md +225 -0
- package/brainstorming/dsl/cli.md +179 -0
- package/brainstorming/dsl/create-compilator.md +25 -0
- package/brainstorming/dsl/dsl-analysis-2.md +166 -0
- package/brainstorming/dsl/dsl-analysis.md +512 -0
- package/brainstorming/dsl/dsl-critique.md +41 -0
- package/brainstorming/fanouts/fanout-subflows-flattened-vs-subruns.md +213 -0
- package/brainstorming/fanouts/fanouts-task-index.md +150 -0
- package/brainstorming/fanouts/fanouts-with-conditions-and-subflows.md +239 -0
- package/brainstorming/subflows/branching.ts.md +38 -0
- package/brainstorming/subflows/subflows-callbacks.ts.md +124 -0
- package/brainstorming/subflows/subflows-classes.ts.md +83 -0
- package/brainstorming/subflows/subflows-flattening-versioned.md +119 -0
- package/brainstorming/subflows/subflows-flattening.md +138 -0
- package/brainstorming/subflows/subflows.md +118 -0
- package/brainstorming/subflows/subruns-table.md +282 -0
- package/brainstorming/subflows/subruns.md +315 -0
- package/brainstorming/versioning/breaking-and-non-breaking-flow-changes.md +259 -0
- package/dist/README.md +11 -0
- package/dist/index.js +123 -0
- package/docs/refactor-edge-worker.md +146 -0
- package/docs/versioning.md +19 -0
- package/eslint.config.cjs +22 -0
- package/out-tsc/vitest/__tests__/runtime/flow.test.d.ts +2 -0
- package/out-tsc/vitest/__tests__/runtime/flow.test.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/runtime/steps.test.d.ts +2 -0
- package/out-tsc/vitest/__tests__/runtime/steps.test.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/runtime/utils.test.d.ts +2 -0
- package/out-tsc/vitest/__tests__/runtime/utils.test.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/types/dsl-types.test-d.d.ts +2 -0
- package/out-tsc/vitest/__tests__/types/dsl-types.test-d.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/types/example-flow.test-d.d.ts +2 -0
- package/out-tsc/vitest/__tests__/types/example-flow.test-d.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/types/extract-flow-input.test-d.d.ts +2 -0
- package/out-tsc/vitest/__tests__/types/extract-flow-input.test-d.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/types/extract-flow-steps.test-d.d.ts +2 -0
- package/out-tsc/vitest/__tests__/types/extract-flow-steps.test-d.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/types/getStepDefinition.test-d.d.ts +2 -0
- package/out-tsc/vitest/__tests__/types/getStepDefinition.test-d.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/types/step-input.test-d.d.ts +2 -0
- package/out-tsc/vitest/__tests__/types/step-input.test-d.d.ts.map +1 -0
- package/out-tsc/vitest/__tests__/types/step-output.test-d.d.ts +2 -0
- package/out-tsc/vitest/__tests__/types/step-output.test-d.d.ts.map +1 -0
- package/out-tsc/vitest/tsconfig.spec.tsbuildinfo +1 -0
- package/out-tsc/vitest/vite.config.d.ts +3 -0
- package/out-tsc/vitest/vite.config.d.ts.map +1 -0
- package/package.json +23 -0
- package/project.json +28 -0
- package/prompts/edge-worker-refactor.md +105 -0
- package/src/dsl.ts +318 -0
- package/src/example-flow.ts +67 -0
- package/src/index.ts +1 -0
- package/src/utils.ts +84 -0
- package/tsconfig.json +13 -0
- package/tsconfig.lib.json +26 -0
- package/tsconfig.spec.json +35 -0
- package/typecheck.log +120 -0
- package/vite.config.ts +57 -0
package/src/dsl.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { validateRuntimeOptions, validateSlug } from './utils.ts';
|
|
2
|
+
|
|
3
|
+
// ========================
|
|
4
|
+
// CORE TYPE DEFINITIONS
|
|
5
|
+
// ========================
|
|
6
|
+
|
|
7
|
+
// JSON type enforcement so we can serialize the results to JSONB columns
|
|
8
|
+
export type Json =
|
|
9
|
+
| string
|
|
10
|
+
| number
|
|
11
|
+
| boolean
|
|
12
|
+
| null
|
|
13
|
+
| Json[]
|
|
14
|
+
| { [key: string]: Json | undefined };
|
|
15
|
+
|
|
16
|
+
// Used to flatten the types of a union of objects for readability
|
|
17
|
+
export type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
|
|
18
|
+
|
|
19
|
+
// ========================
|
|
20
|
+
// FLOW COMPONENT TYPES
|
|
21
|
+
// ========================
|
|
22
|
+
|
|
23
|
+
// Input Types
|
|
24
|
+
export type AnyInput = Json;
|
|
25
|
+
export type AnyOutput = Json;
|
|
26
|
+
|
|
27
|
+
// Step Types
|
|
28
|
+
export type EmptySteps = Record<never, never>;
|
|
29
|
+
export type AnySteps = Record<string, AnyOutput>; // Could use unknown if needed
|
|
30
|
+
|
|
31
|
+
// Dependency Types
|
|
32
|
+
export type EmptyDeps = Record<never, never[]>;
|
|
33
|
+
export type DefaultDeps = Record<string, string[]>;
|
|
34
|
+
export type AnyDeps = Record<string, string[]>;
|
|
35
|
+
|
|
36
|
+
// ========================
|
|
37
|
+
// FLOW TYPE VARIANTS
|
|
38
|
+
// ========================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Represents a Flow that has not steps nor deps defined yet
|
|
42
|
+
*/
|
|
43
|
+
export type EmptyFlow = Flow<AnyInput, EmptySteps, EmptyDeps>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Represents any Flow with flexible input, steps, and dependencies.
|
|
47
|
+
* This type is intentionally more permissive to allow for better type inference
|
|
48
|
+
* in utility types like StepOutput.
|
|
49
|
+
*/
|
|
50
|
+
export type AnyFlow = Flow<any, any, any>;
|
|
51
|
+
|
|
52
|
+
// ========================
|
|
53
|
+
// UTILITY TYPES (with proper constraints)
|
|
54
|
+
// ========================
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Extracts the input type from a Flow
|
|
58
|
+
* @template TFlow - The Flow type to extract from
|
|
59
|
+
*/
|
|
60
|
+
export type ExtractFlowInput<TFlow extends AnyFlow> = TFlow extends Flow<
|
|
61
|
+
infer TI,
|
|
62
|
+
infer _TS,
|
|
63
|
+
infer _TD
|
|
64
|
+
>
|
|
65
|
+
? TI
|
|
66
|
+
: never;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Extracts the steps type from a Flow
|
|
70
|
+
* @template TFlow - The Flow type to extract from
|
|
71
|
+
*/
|
|
72
|
+
export type ExtractFlowSteps<TFlow extends AnyFlow> = TFlow extends Flow<
|
|
73
|
+
infer _TI,
|
|
74
|
+
infer TS,
|
|
75
|
+
infer _TD
|
|
76
|
+
>
|
|
77
|
+
? TS
|
|
78
|
+
: never;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extracts the dependencies type from a Flow
|
|
82
|
+
* @template TFlow - The Flow type to extract from
|
|
83
|
+
*/
|
|
84
|
+
export type ExtractFlowDeps<TFlow extends AnyFlow> = TFlow extends Flow<
|
|
85
|
+
infer _TI,
|
|
86
|
+
infer _TS,
|
|
87
|
+
infer TD
|
|
88
|
+
>
|
|
89
|
+
? TD
|
|
90
|
+
: never;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Extracts the dependencies type from a Flow
|
|
94
|
+
* @template TFlow - The Flow type to extract from
|
|
95
|
+
*/
|
|
96
|
+
type StepDepsOf<
|
|
97
|
+
TFlow extends AnyFlow,
|
|
98
|
+
TStepSlug extends string
|
|
99
|
+
> = TStepSlug extends keyof ExtractFlowDeps<TFlow>
|
|
100
|
+
? ExtractFlowDeps<TFlow>[TStepSlug][number] // The string slugs that TStepSlug depends on
|
|
101
|
+
: never;
|
|
102
|
+
|
|
103
|
+
// Utility type to extract the output type of a step handler from a Flow
|
|
104
|
+
// Usage:
|
|
105
|
+
// StepOutput<typeof flow, 'step1'>
|
|
106
|
+
export type StepOutput<
|
|
107
|
+
TFlow extends AnyFlow,
|
|
108
|
+
TStepSlug extends string
|
|
109
|
+
> = TStepSlug extends keyof ExtractFlowSteps<TFlow>
|
|
110
|
+
? ExtractFlowSteps<TFlow>[TStepSlug]
|
|
111
|
+
: never;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* This ensures that:
|
|
115
|
+
* 1. The run input is always included
|
|
116
|
+
* 2. Only declared dependencies are included
|
|
117
|
+
* 3. No extra properties are allowed
|
|
118
|
+
* Utility type to extract the input type for a specific step in a flow
|
|
119
|
+
*/
|
|
120
|
+
export type StepInput<TFlow extends AnyFlow, TStepSlug extends string> = {
|
|
121
|
+
run: ExtractFlowInput<TFlow>;
|
|
122
|
+
} & {
|
|
123
|
+
[K in Extract<
|
|
124
|
+
keyof ExtractFlowSteps<TFlow>,
|
|
125
|
+
StepDepsOf<TFlow, TStepSlug>
|
|
126
|
+
>]: ExtractFlowSteps<TFlow>[K];
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Runtime options interface
|
|
130
|
+
export interface RuntimeOptions {
|
|
131
|
+
maxAttempts?: number;
|
|
132
|
+
baseDelay?: number;
|
|
133
|
+
timeout?: number;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Define the StepDefinition interface with integrated options
|
|
137
|
+
export interface StepDefinition<
|
|
138
|
+
TInput extends AnyInput,
|
|
139
|
+
TOutput extends AnyOutput
|
|
140
|
+
> {
|
|
141
|
+
slug: string;
|
|
142
|
+
handler: (input: TInput) => TOutput | Promise<TOutput>;
|
|
143
|
+
dependencies: string[];
|
|
144
|
+
options: RuntimeOptions;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Utility type to merge two object types and preserve required properties
|
|
148
|
+
type MergeObjects<T1 extends object, T2 extends object> = T1 & T2;
|
|
149
|
+
|
|
150
|
+
// Flow class definition
|
|
151
|
+
export class Flow<
|
|
152
|
+
TFlowInput extends AnyInput = AnyInput,
|
|
153
|
+
Steps extends AnySteps = EmptySteps,
|
|
154
|
+
StepDependencies extends AnyDeps = EmptyDeps
|
|
155
|
+
> {
|
|
156
|
+
/**
|
|
157
|
+
* Store step definitions with their proper types
|
|
158
|
+
*
|
|
159
|
+
* This is typed as a generic record because TypeScript cannot track the exact relationship
|
|
160
|
+
* between step slugs and their corresponding input/output types at the container level.
|
|
161
|
+
* Type safety is enforced at the method level when adding or retrieving steps.
|
|
162
|
+
*/
|
|
163
|
+
private stepDefinitions: Record<string, StepDefinition<AnyInput, AnyOutput>>;
|
|
164
|
+
private stepOrder: string[];
|
|
165
|
+
public readonly slug: string;
|
|
166
|
+
public readonly options: RuntimeOptions;
|
|
167
|
+
|
|
168
|
+
constructor(
|
|
169
|
+
config: Simplify<{ slug: string } & RuntimeOptions>,
|
|
170
|
+
stepDefinitions: Record<string, StepDefinition<AnyInput, AnyOutput>> = {},
|
|
171
|
+
stepOrder: string[] = []
|
|
172
|
+
) {
|
|
173
|
+
// Extract slug and options separately
|
|
174
|
+
const { slug, ...options } = config;
|
|
175
|
+
|
|
176
|
+
// Validate the slug
|
|
177
|
+
validateSlug(slug);
|
|
178
|
+
|
|
179
|
+
// Validate runtime options (optional for Flow level)
|
|
180
|
+
validateRuntimeOptions(options, { optional: true });
|
|
181
|
+
|
|
182
|
+
this.slug = slug;
|
|
183
|
+
this.options = options;
|
|
184
|
+
this.stepDefinitions = stepDefinitions;
|
|
185
|
+
// Defensive copy of stepOrder
|
|
186
|
+
this.stepOrder = [...stepOrder];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get a specific step definition by slug with proper typing
|
|
191
|
+
* @throws Error if the step with the given slug doesn't exist
|
|
192
|
+
*/
|
|
193
|
+
getStepDefinition<SlugType extends keyof Steps & keyof StepDependencies>(
|
|
194
|
+
slug: SlugType
|
|
195
|
+
): StepDefinition<
|
|
196
|
+
Simplify<
|
|
197
|
+
{
|
|
198
|
+
run: TFlowInput;
|
|
199
|
+
} & {
|
|
200
|
+
[K in StepDependencies[SlugType][number]]: K extends keyof Steps
|
|
201
|
+
? Steps[K]
|
|
202
|
+
: never;
|
|
203
|
+
}
|
|
204
|
+
>,
|
|
205
|
+
Steps[SlugType]
|
|
206
|
+
> {
|
|
207
|
+
// Check if the slug exists in stepDefinitions using a more explicit pattern
|
|
208
|
+
if (!(slug in this.stepDefinitions)) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`Step "${String(slug)}" does not exist in flow "${this.slug}"`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Use a type assertion directive to tell TypeScript that this is safe
|
|
215
|
+
// @ts-expect-error The type system cannot track that this.stepDefinitions[slug] has the correct type
|
|
216
|
+
// but we know it's safe because we only add steps through the strongly-typed `step` method
|
|
217
|
+
return this.stepDefinitions[slug as string];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// SlugType extends keyof Steps & keyof StepDependencies
|
|
221
|
+
step<
|
|
222
|
+
Slug extends string,
|
|
223
|
+
Deps extends Extract<keyof Steps, string> = never,
|
|
224
|
+
RetType extends AnyOutput = AnyOutput
|
|
225
|
+
>(
|
|
226
|
+
opts: Simplify<{ slug: Slug; dependsOn?: Deps[] } & RuntimeOptions>,
|
|
227
|
+
handler: (
|
|
228
|
+
input: Simplify<
|
|
229
|
+
{
|
|
230
|
+
run: TFlowInput;
|
|
231
|
+
} & {
|
|
232
|
+
[K in Deps]: K extends keyof Steps ? Steps[K] : never;
|
|
233
|
+
}
|
|
234
|
+
>
|
|
235
|
+
) => RetType | Promise<RetType>
|
|
236
|
+
): Flow<
|
|
237
|
+
TFlowInput,
|
|
238
|
+
Steps & { [K in Slug]: Awaited<RetType> },
|
|
239
|
+
StepDependencies & { [K in Slug]: Deps[] }
|
|
240
|
+
> {
|
|
241
|
+
type NewSteps = MergeObjects<Steps, { [K in Slug]: Awaited<RetType> }>;
|
|
242
|
+
type NewDependencies = MergeObjects<
|
|
243
|
+
StepDependencies,
|
|
244
|
+
{ [K in Slug]: Deps[] }
|
|
245
|
+
>;
|
|
246
|
+
|
|
247
|
+
const slug = opts.slug as Slug;
|
|
248
|
+
|
|
249
|
+
// Validate the step slug
|
|
250
|
+
validateSlug(slug);
|
|
251
|
+
|
|
252
|
+
if (this.stepDefinitions[slug]) {
|
|
253
|
+
throw new Error(`Step "${slug}" already exists in flow "${this.slug}"`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const dependencies = opts.dependsOn || [];
|
|
257
|
+
// Validate dependencies - check if all referenced steps exist
|
|
258
|
+
if (dependencies.length > 0) {
|
|
259
|
+
for (const dep of dependencies) {
|
|
260
|
+
if (!this.stepDefinitions[dep as string]) {
|
|
261
|
+
throw new Error(`Step "${slug}" depends on undefined step "${dep}"`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Extract RuntimeOptions from opts
|
|
267
|
+
const options: RuntimeOptions = {};
|
|
268
|
+
if (opts.maxAttempts !== undefined) options.maxAttempts = opts.maxAttempts;
|
|
269
|
+
if (opts.baseDelay !== undefined) options.baseDelay = opts.baseDelay;
|
|
270
|
+
if (opts.timeout !== undefined) options.timeout = opts.timeout;
|
|
271
|
+
|
|
272
|
+
// Validate runtime options (optional for step level)
|
|
273
|
+
validateRuntimeOptions(options, { optional: true });
|
|
274
|
+
|
|
275
|
+
// Preserve the exact type of the handler
|
|
276
|
+
const newStepDefinition: StepDefinition<
|
|
277
|
+
Simplify<
|
|
278
|
+
{
|
|
279
|
+
run: TFlowInput;
|
|
280
|
+
} & {
|
|
281
|
+
[K in Deps]: K extends keyof Steps ? Steps[K] : never;
|
|
282
|
+
}
|
|
283
|
+
>,
|
|
284
|
+
RetType
|
|
285
|
+
> = {
|
|
286
|
+
slug,
|
|
287
|
+
handler: handler as (
|
|
288
|
+
input: Simplify<
|
|
289
|
+
{
|
|
290
|
+
run: TFlowInput;
|
|
291
|
+
} & {
|
|
292
|
+
[K in Deps]: K extends keyof Steps ? Steps[K] : never;
|
|
293
|
+
}
|
|
294
|
+
>
|
|
295
|
+
) => RetType | Promise<RetType>,
|
|
296
|
+
dependencies: dependencies as string[],
|
|
297
|
+
options,
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const newStepDefinitions = {
|
|
301
|
+
...this.stepDefinitions,
|
|
302
|
+
[slug]: newStepDefinition,
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// Create a new stepOrder array with the new slug appended
|
|
306
|
+
const newStepOrder = [...this.stepOrder, slug];
|
|
307
|
+
|
|
308
|
+
// Create a new flow with the same slug and options but with updated type parameters
|
|
309
|
+
// We need to use type assertions here because TypeScript cannot track the exact relationship
|
|
310
|
+
// between the specific step definition types and the generic Flow type parameters
|
|
311
|
+
// This is safe because we're constructing the newStepDefinitions in a type-safe way above
|
|
312
|
+
return new Flow<TFlowInput, NewSteps, NewDependencies>(
|
|
313
|
+
{ slug: this.slug, ...this.options },
|
|
314
|
+
newStepDefinitions as Record<string, StepDefinition<AnyInput, AnyOutput>>,
|
|
315
|
+
newStepOrder
|
|
316
|
+
) as Flow<TFlowInput, NewSteps, NewDependencies>;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Flow } from './dsl.ts';
|
|
2
|
+
|
|
3
|
+
// Provide a type for the input of the Flow
|
|
4
|
+
type Input = {
|
|
5
|
+
url: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const AnalyzeWebsite = new Flow<Input>({
|
|
9
|
+
slug: 'analyze_website',
|
|
10
|
+
maxAttempts: 3,
|
|
11
|
+
baseDelay: 5,
|
|
12
|
+
timeout: 10,
|
|
13
|
+
})
|
|
14
|
+
.step(
|
|
15
|
+
{ slug: 'website' },
|
|
16
|
+
async (input) => await scrapeWebsite(input.run.url)
|
|
17
|
+
)
|
|
18
|
+
.step(
|
|
19
|
+
{ slug: 'sentiment', dependsOn: ['website'], timeout: 30, maxAttempts: 5 },
|
|
20
|
+
async (input) => await analyzeSentiment(input.website.content)
|
|
21
|
+
)
|
|
22
|
+
.step(
|
|
23
|
+
{ slug: 'summary', dependsOn: ['website'] },
|
|
24
|
+
async (input) => await summarizeWithAI(input.website.content)
|
|
25
|
+
)
|
|
26
|
+
.step(
|
|
27
|
+
{ slug: 'saveToDb', dependsOn: ['sentiment', 'summary'] },
|
|
28
|
+
async (input) => {
|
|
29
|
+
const results = await saveToDb({
|
|
30
|
+
websiteUrl: input.run.url,
|
|
31
|
+
sentiment: input.sentiment.score,
|
|
32
|
+
summary: input.summary.aiSummary,
|
|
33
|
+
});
|
|
34
|
+
return results.status;
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
/***********************************************************************
|
|
39
|
+
****** functions *******************************************************
|
|
40
|
+
***********************************************************************/
|
|
41
|
+
|
|
42
|
+
async function scrapeWebsite(url: string) {
|
|
43
|
+
return {
|
|
44
|
+
content: `Lorem ipsum ${url.length}`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const analyzeSentiment = async (_content: string) => {
|
|
49
|
+
return {
|
|
50
|
+
score: Math.random(),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const summarizeWithAI = async (content: string) => {
|
|
54
|
+
return {
|
|
55
|
+
aiSummary: `Lorem ipsum ${content.length}`,
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const saveToDb = async (_input: {
|
|
60
|
+
websiteUrl: string;
|
|
61
|
+
sentiment: number;
|
|
62
|
+
summary: string;
|
|
63
|
+
}) => {
|
|
64
|
+
return {
|
|
65
|
+
status: 'success',
|
|
66
|
+
};
|
|
67
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dsl.ts';
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates a slug string according to the following rules:
|
|
3
|
+
* - Cannot start with a number
|
|
4
|
+
* - Cannot start with an underscore
|
|
5
|
+
* - Cannot contain spaces
|
|
6
|
+
* - Cannot contain special characters like /, :, ?, #
|
|
7
|
+
* - Cannot be longer than 128 characters
|
|
8
|
+
*
|
|
9
|
+
* @param slug The slug string to validate
|
|
10
|
+
* @throws Error if the slug is invalid
|
|
11
|
+
*/
|
|
12
|
+
export function validateSlug(slug: string): void {
|
|
13
|
+
if (slug.length > 128) {
|
|
14
|
+
throw new Error(`Slug cannot be longer than 128 characters`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (/^\d/.test(slug)) {
|
|
18
|
+
throw new Error(`Slug cannot start with a number`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (/^_/.test(slug)) {
|
|
22
|
+
throw new Error(`Slug cannot start with an underscore`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (/\s/.test(slug)) {
|
|
26
|
+
throw new Error(`Slug cannot contain spaces`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (/[/:#\-?]/.test(slug)) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Slug cannot contain special characters like /, :, ?, #, -`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options for validating runtime options
|
|
38
|
+
*/
|
|
39
|
+
export interface ValidateRuntimeOptionsOpts {
|
|
40
|
+
optional?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validates runtime options according to the following rules:
|
|
45
|
+
* - maxAttempts should be >= 1
|
|
46
|
+
* - baseDelay should be >= 1
|
|
47
|
+
* - timeout should be >= 3
|
|
48
|
+
*
|
|
49
|
+
* @param options The runtime options to validate
|
|
50
|
+
* @param opts Configuration options for validation
|
|
51
|
+
* @param opts.optional If true, allows options to be null or undefined
|
|
52
|
+
* @throws Error if any runtime option is invalid
|
|
53
|
+
*/
|
|
54
|
+
export function validateRuntimeOptions(
|
|
55
|
+
options: { maxAttempts?: number; baseDelay?: number; timeout?: number },
|
|
56
|
+
opts: ValidateRuntimeOptionsOpts = { optional: false }
|
|
57
|
+
): void {
|
|
58
|
+
const { maxAttempts, baseDelay, timeout } = options;
|
|
59
|
+
|
|
60
|
+
// If optional is true, skip validation for undefined/null values
|
|
61
|
+
if (maxAttempts !== undefined && maxAttempts !== null) {
|
|
62
|
+
if (maxAttempts < 1) {
|
|
63
|
+
throw new Error('maxAttempts must be greater than or equal to 1');
|
|
64
|
+
}
|
|
65
|
+
} else if (!opts.optional) {
|
|
66
|
+
throw new Error('maxAttempts is required');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (baseDelay !== undefined && baseDelay !== null) {
|
|
70
|
+
if (baseDelay < 1) {
|
|
71
|
+
throw new Error('baseDelay must be greater than or equal to 1');
|
|
72
|
+
}
|
|
73
|
+
} else if (!opts.optional) {
|
|
74
|
+
throw new Error('baseDelay is required');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (timeout !== undefined && timeout !== null) {
|
|
78
|
+
if (timeout < 3) {
|
|
79
|
+
throw new Error('timeout must be greater than or equal to 3');
|
|
80
|
+
}
|
|
81
|
+
} else if (!opts.optional) {
|
|
82
|
+
throw new Error('timeout is required');
|
|
83
|
+
}
|
|
84
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "dist",
|
|
5
|
+
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
|
|
6
|
+
"emitDeclarationOnly": false,
|
|
7
|
+
"types": ["node", "vite/client"],
|
|
8
|
+
"allowImportingTsExtensions": true
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*.ts"],
|
|
11
|
+
"references": [],
|
|
12
|
+
"exclude": [
|
|
13
|
+
"vite.config.ts",
|
|
14
|
+
"vite.config.mts",
|
|
15
|
+
"vitest.config.ts",
|
|
16
|
+
"vitest.config.mts",
|
|
17
|
+
"src/**/*.test.ts",
|
|
18
|
+
"src/**/*.spec.ts",
|
|
19
|
+
"src/**/*.test.tsx",
|
|
20
|
+
"src/**/*.spec.tsx",
|
|
21
|
+
"src/**/*.test.js",
|
|
22
|
+
"src/**/*.spec.js",
|
|
23
|
+
"src/**/*.test.jsx",
|
|
24
|
+
"src/**/*.spec.jsx"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./out-tsc/vitest",
|
|
5
|
+
"types": [
|
|
6
|
+
"vitest/globals",
|
|
7
|
+
"vitest/importMeta",
|
|
8
|
+
"vite/client",
|
|
9
|
+
"node",
|
|
10
|
+
"vitest"
|
|
11
|
+
],
|
|
12
|
+
"allowImportingTsExtensions": true
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"vite.config.ts",
|
|
16
|
+
"vite.config.mts",
|
|
17
|
+
"vitest.config.ts",
|
|
18
|
+
"vitest.config.mts",
|
|
19
|
+
"src/**/*.test.ts",
|
|
20
|
+
"src/**/*.spec.ts",
|
|
21
|
+
"src/**/*.test.tsx",
|
|
22
|
+
"src/**/*.spec.tsx",
|
|
23
|
+
"src/**/*.test.js",
|
|
24
|
+
"src/**/*.spec.js",
|
|
25
|
+
"src/**/*.test.jsx",
|
|
26
|
+
"src/**/*.spec.jsx",
|
|
27
|
+
"src/**/*.d.ts",
|
|
28
|
+
"__tests__/**/*.ts"
|
|
29
|
+
],
|
|
30
|
+
"references": [
|
|
31
|
+
{
|
|
32
|
+
"path": "./tsconfig.lib.json"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
package/typecheck.log
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
|
|
2
|
+
> nx run dsl:typecheck
|
|
3
|
+
|
|
4
|
+
> tsc --build --emitDeclarationOnly --pretty --verbose
|
|
5
|
+
|
|
6
|
+
[[90m9:57:14 PM[0m] Projects in this build:
|
|
7
|
+
* tsconfig.lib.json
|
|
8
|
+
* tsconfig.spec.json
|
|
9
|
+
* tsconfig.json
|
|
10
|
+
|
|
11
|
+
[[90m9:57:14 PM[0m] Project 'tsconfig.lib.json' is up to date because newest input 'src/dsl.ts' is older than output 'dist/tsconfig.lib.tsbuildinfo'
|
|
12
|
+
|
|
13
|
+
[[90m9:57:14 PM[0m] Project 'tsconfig.spec.json' is out of date because buildinfo file 'out-tsc/vitest/tsconfig.spec.tsbuildinfo' indicates that program needs to report errors.
|
|
14
|
+
|
|
15
|
+
[[90m9:57:14 PM[0m] Building project '/home/jumski/Code/pgflow-dev/pgflow/pkgs/dsl/tsconfig.spec.json'...
|
|
16
|
+
|
|
17
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m112[0m:[93m37[0m - [91merror[0m[90m TS2344: [0mType 'Flow<{ input: string; }, EmptySteps & { step1: { value: number; text: string; }; } & { step2: { flag: true; }; } & { step3: "plain string"; }, EmptyDeps & { step1: never[]; } & { ...; } & { ...; }>' does not satisfy the constraint 'AnyFlow'.
|
|
18
|
+
The types returned by 'getStepDefinition(...)' are incompatible between these types.
|
|
19
|
+
Type 'StepDefinition<{ [x: string]: any; run: { input: string; }; }, any>' is not assignable to type 'StepDefinition<{ [x: string]: Json; run: Json; }, Json>'.
|
|
20
|
+
Type '{ [x: string]: Json; run: Json; }' is not assignable to type '{ [x: string]: any; run: { input: string; }; }'.
|
|
21
|
+
Types of property 'run' are incompatible.
|
|
22
|
+
Type 'Json' is not assignable to type '{ input: string; }'.
|
|
23
|
+
Type 'null' is not assignable to type '{ input: string; }'.
|
|
24
|
+
|
|
25
|
+
[7m112[0m type Step1Output = StepOutput<typeof flow, 'step1'>;
|
|
26
|
+
[7m [0m [91m ~~~~~~~~~~~[0m
|
|
27
|
+
|
|
28
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m113[0m:[93m49[0m - [91merror[0m[90m TS2344: [0mType '{ value: number; text: string; }' does not satisfy the constraint '"Expected: ..., Actual: never"'.
|
|
29
|
+
|
|
30
|
+
[7m113[0m expectTypeOf<Step1Output>().toMatchTypeOf<{
|
|
31
|
+
[7m [0m [91m ~[0m
|
|
32
|
+
[7m114[0m value: number;
|
|
33
|
+
[7m [0m [91m~~~~~~~~~~~~~~~~~~~~~~[0m
|
|
34
|
+
[7m115[0m text: string;
|
|
35
|
+
[7m [0m [91m~~~~~~~~~~~~~~~~~~~~~[0m
|
|
36
|
+
[7m116[0m }>();
|
|
37
|
+
[7m [0m [91m~~~~~~~[0m
|
|
38
|
+
|
|
39
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m118[0m:[93m37[0m - [91merror[0m[90m TS2344: [0mType 'Flow<{ input: string; }, EmptySteps & { step1: { value: number; text: string; }; } & { step2: { flag: true; }; } & { step3: "plain string"; }, EmptyDeps & { step1: never[]; } & { ...; } & { ...; }>' does not satisfy the constraint 'AnyFlow'.
|
|
40
|
+
The types returned by 'getStepDefinition(...)' are incompatible between these types.
|
|
41
|
+
Type 'StepDefinition<{ [x: string]: any; run: { input: string; }; }, any>' is not assignable to type 'StepDefinition<{ [x: string]: Json; run: Json; }, Json>'.
|
|
42
|
+
Type '{ [x: string]: Json; run: Json; }' is not assignable to type '{ [x: string]: any; run: { input: string; }; }'.
|
|
43
|
+
Types of property 'run' are incompatible.
|
|
44
|
+
Type 'Json' is not assignable to type '{ input: string; }'.
|
|
45
|
+
Type 'null' is not assignable to type '{ input: string; }'.
|
|
46
|
+
|
|
47
|
+
[7m118[0m type Step2Output = StepOutput<typeof flow, 'step2'>;
|
|
48
|
+
[7m [0m [91m ~~~~~~~~~~~[0m
|
|
49
|
+
|
|
50
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m119[0m:[93m49[0m - [91merror[0m[90m TS2344: [0mType '{ flag: boolean; }' does not satisfy the constraint '"Expected: ..., Actual: never"'.
|
|
51
|
+
|
|
52
|
+
[7m119[0m expectTypeOf<Step2Output>().toMatchTypeOf<{ flag: boolean }>();
|
|
53
|
+
[7m [0m [91m ~~~~~~~~~~~~~~~~~[0m
|
|
54
|
+
|
|
55
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m121[0m:[93m37[0m - [91merror[0m[90m TS2344: [0mType 'Flow<{ input: string; }, EmptySteps & { step1: { value: number; text: string; }; } & { step2: { flag: true; }; } & { step3: "plain string"; }, EmptyDeps & { step1: never[]; } & { ...; } & { ...; }>' does not satisfy the constraint 'AnyFlow'.
|
|
56
|
+
The types returned by 'getStepDefinition(...)' are incompatible between these types.
|
|
57
|
+
Type 'StepDefinition<{ [x: string]: any; run: { input: string; }; }, any>' is not assignable to type 'StepDefinition<{ [x: string]: Json; run: Json; }, Json>'.
|
|
58
|
+
Type '{ [x: string]: Json; run: Json; }' is not assignable to type '{ [x: string]: any; run: { input: string; }; }'.
|
|
59
|
+
Types of property 'run' are incompatible.
|
|
60
|
+
Type 'Json' is not assignable to type '{ input: string; }'.
|
|
61
|
+
Type 'null' is not assignable to type '{ input: string; }'.
|
|
62
|
+
|
|
63
|
+
[7m121[0m type Step3Output = StepOutput<typeof flow, 'step3'>;
|
|
64
|
+
[7m [0m [91m ~~~~~~~~~~~[0m
|
|
65
|
+
|
|
66
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m122[0m:[93m49[0m - [91merror[0m[90m TS2344: [0mType 'string' does not satisfy the constraint '"Expected: string, Actual: never"'.
|
|
67
|
+
|
|
68
|
+
[7m122[0m expectTypeOf<Step3Output>().toMatchTypeOf<string>();
|
|
69
|
+
[7m [0m [91m ~~~~~~[0m
|
|
70
|
+
|
|
71
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m124[0m:[93m43[0m - [91merror[0m[90m TS2344: [0mType 'Flow<{ input: string; }, EmptySteps & { step1: { value: number; text: string; }; } & { step2: { flag: true; }; } & { step3: "plain string"; }, EmptyDeps & { step1: never[]; } & { ...; } & { ...; }>' does not satisfy the constraint 'AnyFlow'.
|
|
72
|
+
The types returned by 'getStepDefinition(...)' are incompatible between these types.
|
|
73
|
+
Type 'StepDefinition<{ [x: string]: any; run: { input: string; }; }, any>' is not assignable to type 'StepDefinition<{ [x: string]: Json; run: Json; }, Json>'.
|
|
74
|
+
Type '{ [x: string]: Json; run: Json; }' is not assignable to type '{ [x: string]: any; run: { input: string; }; }'.
|
|
75
|
+
Types of property 'run' are incompatible.
|
|
76
|
+
Type 'Json' is not assignable to type '{ input: string; }'.
|
|
77
|
+
Type 'null' is not assignable to type '{ input: string; }'.
|
|
78
|
+
|
|
79
|
+
[7m124[0m type NonExistentOutput = StepOutput<typeof flow, 'nonExistentStep'>;
|
|
80
|
+
[7m [0m [91m ~~~~~~~~~~~[0m
|
|
81
|
+
|
|
82
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m144[0m:[93m43[0m - [91merror[0m[90m TS2344: [0mType 'Flow<{ id: number; }, EmptySteps & { complexStep: { data: { items: { id: number; name: string; }[]; metadata: { count: number; lastUpdated: string; }; }; }; }, EmptyDeps & { complexStep: never[]; }>' does not satisfy the constraint 'AnyFlow'.
|
|
83
|
+
The types returned by 'getStepDefinition(...)' are incompatible between these types.
|
|
84
|
+
Type 'StepDefinition<{ [x: string]: any; run: { id: number; }; }, any>' is not assignable to type 'StepDefinition<{ [x: string]: Json; run: Json; }, Json>'.
|
|
85
|
+
Type '{ [x: string]: Json; run: Json; }' is not assignable to type '{ [x: string]: any; run: { id: number; }; }'.
|
|
86
|
+
Types of property 'run' are incompatible.
|
|
87
|
+
Type 'Json' is not assignable to type '{ id: number; }'.
|
|
88
|
+
Type 'null' is not assignable to type '{ id: number; }'.
|
|
89
|
+
|
|
90
|
+
[7m144[0m type ComplexStepOutput = StepOutput<typeof complexFlow, 'complexStep'>;
|
|
91
|
+
[7m [0m [91m ~~~~~~~~~~~~~~~~~~[0m
|
|
92
|
+
|
|
93
|
+
[96m__tests__/types/dsl-types.test-d.ts[0m:[93m145[0m:[93m55[0m - [91merror[0m[90m TS2344: [0mType '{ data: { items: { id: number; name: string; }[]; metadata: { count: number; lastUpdated: string; }; }; }' does not satisfy the constraint '"Expected: ..., Actual: never"'.
|
|
94
|
+
|
|
95
|
+
[7m145[0m expectTypeOf<ComplexStepOutput>().toMatchTypeOf<{
|
|
96
|
+
[7m [0m [91m ~[0m
|
|
97
|
+
[7m146[0m data: {
|
|
98
|
+
[7m [0m [91m~~~~~~~~~~~~~~~[0m
|
|
99
|
+
[7m...[0m
|
|
100
|
+
[7m152[0m };
|
|
101
|
+
[7m [0m [91m~~~~~~~~~~[0m
|
|
102
|
+
[7m153[0m }>();
|
|
103
|
+
[7m [0m [91m~~~~~~~[0m
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
Found 9 errors.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
NX Running target typecheck for project dsl failed
|
|
112
|
+
|
|
113
|
+
Failed tasks:
|
|
114
|
+
|
|
115
|
+
- dsl:typecheck
|
|
116
|
+
|
|
117
|
+
Hint: run the command with --verbose for more details.
|
|
118
|
+
|
|
119
|
+
View structured, searchable error logs at https://nx.app/runs/7SrNf6iuVJ
|
|
120
|
+
|