@workflow/core 4.0.1-beta.0

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.
Files changed (143) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +3 -0
  3. package/dist/builtins.d.ts +4 -0
  4. package/dist/builtins.d.ts.map +1 -0
  5. package/dist/builtins.js +13 -0
  6. package/dist/builtins.js.map +1 -0
  7. package/dist/create-hook.d.ts +123 -0
  8. package/dist/create-hook.d.ts.map +1 -0
  9. package/dist/create-hook.js +32 -0
  10. package/dist/create-hook.js.map +1 -0
  11. package/dist/define-hook.d.ts +53 -0
  12. package/dist/define-hook.d.ts.map +1 -0
  13. package/dist/define-hook.js +59 -0
  14. package/dist/define-hook.js.map +1 -0
  15. package/dist/events-consumer.d.ts +35 -0
  16. package/dist/events-consumer.d.ts.map +1 -0
  17. package/dist/events-consumer.js +72 -0
  18. package/dist/events-consumer.js.map +1 -0
  19. package/dist/global.d.ts +29 -0
  20. package/dist/global.d.ts.map +1 -0
  21. package/dist/global.js +50 -0
  22. package/dist/global.js.map +1 -0
  23. package/dist/index.d.ts +18 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +18 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/logger.d.ts +31 -0
  28. package/dist/logger.d.ts.map +1 -0
  29. package/dist/logger.js +32 -0
  30. package/dist/logger.js.map +1 -0
  31. package/dist/observability.d.ts +24 -0
  32. package/dist/observability.d.ts.map +1 -0
  33. package/dist/observability.js +126 -0
  34. package/dist/observability.js.map +1 -0
  35. package/dist/parse-name.d.ts +25 -0
  36. package/dist/parse-name.d.ts.map +1 -0
  37. package/dist/parse-name.js +40 -0
  38. package/dist/parse-name.js.map +1 -0
  39. package/dist/private.d.ts +26 -0
  40. package/dist/private.d.ts.map +1 -0
  41. package/dist/private.js +17 -0
  42. package/dist/private.js.map +1 -0
  43. package/dist/runtime/resume-hook.d.ts +71 -0
  44. package/dist/runtime/resume-hook.d.ts.map +1 -0
  45. package/dist/runtime/resume-hook.js +180 -0
  46. package/dist/runtime/resume-hook.js.map +1 -0
  47. package/dist/runtime/start.d.ts +28 -0
  48. package/dist/runtime/start.d.ts.map +1 -0
  49. package/dist/runtime/start.js +57 -0
  50. package/dist/runtime/start.js.map +1 -0
  51. package/dist/runtime/world.d.ts +24 -0
  52. package/dist/runtime/world.d.ts.map +1 -0
  53. package/dist/runtime/world.js +84 -0
  54. package/dist/runtime/world.js.map +1 -0
  55. package/dist/runtime.d.ts +121 -0
  56. package/dist/runtime.d.ts.map +1 -0
  57. package/dist/runtime.js +554 -0
  58. package/dist/runtime.js.map +1 -0
  59. package/dist/schemas.d.ts +29 -0
  60. package/dist/schemas.d.ts.map +1 -0
  61. package/dist/schemas.js +15 -0
  62. package/dist/schemas.js.map +1 -0
  63. package/dist/serialization.d.ts +188 -0
  64. package/dist/serialization.d.ts.map +1 -0
  65. package/dist/serialization.js +725 -0
  66. package/dist/serialization.js.map +1 -0
  67. package/dist/step/context-storage.d.ts +8 -0
  68. package/dist/step/context-storage.d.ts.map +1 -0
  69. package/dist/step/context-storage.js +3 -0
  70. package/dist/step/context-storage.js.map +1 -0
  71. package/dist/step/get-step-metadata.d.ts +38 -0
  72. package/dist/step/get-step-metadata.d.ts.map +1 -0
  73. package/dist/step/get-step-metadata.js +14 -0
  74. package/dist/step/get-step-metadata.js.map +1 -0
  75. package/dist/step/get-workflow-metadata.d.ts +7 -0
  76. package/dist/step/get-workflow-metadata.d.ts.map +1 -0
  77. package/dist/step/get-workflow-metadata.js +12 -0
  78. package/dist/step/get-workflow-metadata.js.map +1 -0
  79. package/dist/step.d.ts +4 -0
  80. package/dist/step.d.ts.map +1 -0
  81. package/dist/step.js +92 -0
  82. package/dist/step.js.map +1 -0
  83. package/dist/symbols.d.ts +9 -0
  84. package/dist/symbols.d.ts.map +1 -0
  85. package/dist/symbols.js +9 -0
  86. package/dist/symbols.js.map +1 -0
  87. package/dist/telemetry/semantic-conventions.d.ts +175 -0
  88. package/dist/telemetry/semantic-conventions.d.ts.map +1 -0
  89. package/dist/telemetry/semantic-conventions.js +121 -0
  90. package/dist/telemetry/semantic-conventions.js.map +1 -0
  91. package/dist/telemetry.d.ts +24 -0
  92. package/dist/telemetry.d.ts.map +1 -0
  93. package/dist/telemetry.js +121 -0
  94. package/dist/telemetry.js.map +1 -0
  95. package/dist/types.d.ts +10 -0
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +39 -0
  98. package/dist/types.js.map +1 -0
  99. package/dist/util.d.ts +43 -0
  100. package/dist/util.d.ts.map +1 -0
  101. package/dist/util.js +76 -0
  102. package/dist/util.js.map +1 -0
  103. package/dist/vm/index.d.ts +17 -0
  104. package/dist/vm/index.d.ts.map +1 -0
  105. package/dist/vm/index.js +93 -0
  106. package/dist/vm/index.js.map +1 -0
  107. package/dist/vm/uuid.d.ts +10 -0
  108. package/dist/vm/uuid.d.ts.map +1 -0
  109. package/dist/vm/uuid.js +30 -0
  110. package/dist/vm/uuid.js.map +1 -0
  111. package/dist/workflow/create-hook.d.ts +7 -0
  112. package/dist/workflow/create-hook.d.ts.map +1 -0
  113. package/dist/workflow/create-hook.js +22 -0
  114. package/dist/workflow/create-hook.js.map +1 -0
  115. package/dist/workflow/define-hook.d.ts +10 -0
  116. package/dist/workflow/define-hook.d.ts.map +1 -0
  117. package/dist/workflow/define-hook.js +15 -0
  118. package/dist/workflow/define-hook.js.map +1 -0
  119. package/dist/workflow/get-workflow-metadata.d.ts +17 -0
  120. package/dist/workflow/get-workflow-metadata.d.ts.map +1 -0
  121. package/dist/workflow/get-workflow-metadata.js +11 -0
  122. package/dist/workflow/get-workflow-metadata.js.map +1 -0
  123. package/dist/workflow/hook.d.ts +4 -0
  124. package/dist/workflow/hook.d.ts.map +1 -0
  125. package/dist/workflow/hook.js +101 -0
  126. package/dist/workflow/hook.js.map +1 -0
  127. package/dist/workflow/index.d.ts +10 -0
  128. package/dist/workflow/index.d.ts.map +1 -0
  129. package/dist/workflow/index.js +14 -0
  130. package/dist/workflow/index.js.map +1 -0
  131. package/dist/workflow/writable-stream.d.ts +3 -0
  132. package/dist/workflow/writable-stream.d.ts.map +1 -0
  133. package/dist/workflow/writable-stream.js +12 -0
  134. package/dist/workflow/writable-stream.js.map +1 -0
  135. package/dist/workflow.d.ts +3 -0
  136. package/dist/workflow.d.ts.map +1 -0
  137. package/dist/workflow.js +454 -0
  138. package/dist/workflow.js.map +1 -0
  139. package/dist/writable-stream.d.ts +22 -0
  140. package/dist/writable-stream.d.ts.map +1 -0
  141. package/dist/writable-stream.js +16 -0
  142. package/dist/writable-stream.js.map +1 -0
  143. package/package.json +73 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/workflow/hook.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAI3D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAIjE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,2BAA2B,IAChC,CAAC,GAAG,GAAG,EAAE,UAAS,WAAgB,KAAG,IAAI,CAAC,CAAC,CAAC,CAiI5E"}
@@ -0,0 +1,101 @@
1
+ import { EventConsumerResult } from '../events-consumer.js';
2
+ import { WorkflowSuspension } from '../global.js';
3
+ import { webhookLogger } from '../logger.js';
4
+ import { hydrateStepReturnValue } from '../serialization.js';
5
+ import { withResolvers } from '../util.js';
6
+ export function createCreateHook(ctx) {
7
+ return function createHookImpl(options = {}) {
8
+ // Generate hook ID and token
9
+ const correlationId = `hook_${ctx.generateUlid()}`;
10
+ const token = options.token ?? ctx.generateNanoid();
11
+ // Add hook creation to invocations queue
12
+ ctx.invocationsQueue.push({
13
+ type: 'hook',
14
+ correlationId,
15
+ token,
16
+ metadata: options.metadata,
17
+ });
18
+ // Queue of hook events that have been received but not yet processed
19
+ const payloadsQueue = [];
20
+ // Queue of promises that resolve to the next hook payload
21
+ const promises = [];
22
+ let eventLogEmpty = false;
23
+ webhookLogger.debug('Hook consumer setup', { correlationId, token });
24
+ ctx.eventsConsumer.subscribe((event) => {
25
+ // If there are no events and there are promises waiting,
26
+ // it means the hook has been awaited, but an incoming payload has not yet been received.
27
+ // In this case, the workflow should be suspended until the hook is resumed.
28
+ if (!event) {
29
+ eventLogEmpty = true;
30
+ if (promises.length > 0) {
31
+ setTimeout(() => {
32
+ ctx.onWorkflowError(new WorkflowSuspension(ctx.invocationsQueue, ctx.globalThis));
33
+ }, 0);
34
+ return EventConsumerResult.Finished;
35
+ }
36
+ }
37
+ // Check for hook_created event to remove this hook from the queue if it was already created
38
+ if (event?.eventType === 'hook_created' &&
39
+ event.correlationId === correlationId) {
40
+ // Remove this hook from the invocations queue if it exists
41
+ const index = ctx.invocationsQueue.findIndex((item) => item.type === 'hook' && item.correlationId === correlationId);
42
+ if (index !== -1) {
43
+ ctx.invocationsQueue.splice(index, 1);
44
+ }
45
+ return EventConsumerResult.Consumed;
46
+ }
47
+ if (event?.eventType === 'hook_received' &&
48
+ event.correlationId === correlationId) {
49
+ if (promises.length > 0) {
50
+ const next = promises.shift();
51
+ if (next) {
52
+ // Reconstruct the payload from the event data
53
+ const payload = hydrateStepReturnValue(event.eventData.payload, ctx.globalThis);
54
+ next.resolve(payload);
55
+ }
56
+ }
57
+ else {
58
+ payloadsQueue.push(event);
59
+ }
60
+ return EventConsumerResult.Consumed;
61
+ }
62
+ return EventConsumerResult.NotConsumed;
63
+ });
64
+ // Helper function to create a new promise that waits for the next hook payload
65
+ function createHookPromise() {
66
+ const resolvers = withResolvers();
67
+ if (payloadsQueue.length > 0) {
68
+ const nextPayload = payloadsQueue.shift();
69
+ if (nextPayload) {
70
+ const payload = hydrateStepReturnValue(nextPayload.eventData.payload, ctx.globalThis);
71
+ resolvers.resolve(payload);
72
+ return resolvers.promise;
73
+ }
74
+ }
75
+ if (eventLogEmpty) {
76
+ // If the event log is already empty then we know the hook will not be resolved.
77
+ // Treat this case as a "step not run" scenario and suspend the workflow.
78
+ setTimeout(() => {
79
+ ctx.onWorkflowError(new WorkflowSuspension(ctx.invocationsQueue, ctx.globalThis));
80
+ }, 0);
81
+ }
82
+ promises.push(resolvers);
83
+ return resolvers.promise;
84
+ }
85
+ const hook = {
86
+ token,
87
+ // biome-ignore lint/suspicious/noThenProperty: Intentionally thenable
88
+ then(onfulfilled, onrejected) {
89
+ return createHookPromise().then(onfulfilled, onrejected);
90
+ },
91
+ // Support `for await (const payload of hook) { … }` syntax
92
+ async *[Symbol.asyncIterator]() {
93
+ while (true) {
94
+ yield await this;
95
+ }
96
+ },
97
+ };
98
+ return hook;
99
+ };
100
+ }
101
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/workflow/hook.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAA6B,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,UAAU,gBAAgB,CAAC,GAAgC;IAC/D,OAAO,SAAS,cAAc,CAAU,UAAuB,EAAE;QAC/D,6BAA6B;QAC7B,MAAM,aAAa,GAAG,QAAQ,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC;QACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;QAEpD,yCAAyC;QACzC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC;YACxB,IAAI,EAAE,MAAM;YACZ,aAAa;YACb,KAAK;YACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAC;QAEH,qEAAqE;QACrE,MAAM,aAAa,GAAwB,EAAE,CAAC;QAE9C,0DAA0D;QAC1D,MAAM,QAAQ,GAA8B,EAAE,CAAC;QAE/C,IAAI,aAAa,GAAG,KAAK,CAAC;QAE1B,aAAa,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;QACrE,GAAG,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YACrC,yDAAyD;YACzD,yFAAyF;YACzF,4EAA4E;YAC5E,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,aAAa,GAAG,IAAI,CAAC;gBAErB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACxB,UAAU,CAAC,GAAG,EAAE;wBACd,GAAG,CAAC,eAAe,CACjB,IAAI,kBAAkB,CAAC,GAAG,CAAC,gBAAgB,EAAE,GAAG,CAAC,UAAU,CAAC,CAC7D,CAAC;oBACJ,CAAC,EAAE,CAAC,CAAC,CAAC;oBACN,OAAO,mBAAmB,CAAC,QAAQ,CAAC;gBACtC,CAAC;YACH,CAAC;YAED,4FAA4F;YAC5F,IACE,KAAK,EAAE,SAAS,KAAK,cAAc;gBACnC,KAAK,CAAC,aAAa,KAAK,aAAa,EACrC,CAAC;gBACD,2DAA2D;gBAC3D,MAAM,KAAK,GAAG,GAAG,CAAC,gBAAgB,CAAC,SAAS,CAC1C,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,aAAa,KAAK,aAAa,CACvE,CAAC;gBACF,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;oBACjB,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBACxC,CAAC;gBACD,OAAO,mBAAmB,CAAC,QAAQ,CAAC;YACtC,CAAC;YAED,IACE,KAAK,EAAE,SAAS,KAAK,eAAe;gBACpC,KAAK,CAAC,aAAa,KAAK,aAAa,EACrC,CAAC;gBACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACxB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;oBAC9B,IAAI,IAAI,EAAE,CAAC;wBACT,8CAA8C;wBAC9C,MAAM,OAAO,GAAG,sBAAsB,CACpC,KAAK,CAAC,SAAS,CAAC,OAAO,EACvB,GAAG,CAAC,UAAU,CACf,CAAC;wBACF,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;oBACxB,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC;gBAED,OAAO,mBAAmB,CAAC,QAAQ,CAAC;YACtC,CAAC;YAED,OAAO,mBAAmB,CAAC,WAAW,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,+EAA+E;QAC/E,SAAS,iBAAiB;YACxB,MAAM,SAAS,GAAG,aAAa,EAAK,CAAC;YACrC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC;gBAC1C,IAAI,WAAW,EAAE,CAAC;oBAChB,MAAM,OAAO,GAAG,sBAAsB,CACpC,WAAW,CAAC,SAAS,CAAC,OAAO,EAC7B,GAAG,CAAC,UAAU,CACf,CAAC;oBACF,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;oBAC3B,OAAO,SAAS,CAAC,OAAO,CAAC;gBAC3B,CAAC;YACH,CAAC;YAED,IAAI,aAAa,EAAE,CAAC;gBAClB,gFAAgF;gBAChF,yEAAyE;gBACzE,UAAU,CAAC,GAAG,EAAE;oBACd,GAAG,CAAC,eAAe,CACjB,IAAI,kBAAkB,CAAC,GAAG,CAAC,gBAAgB,EAAE,GAAG,CAAC,UAAU,CAAC,CAC7D,CAAC;gBACJ,CAAC,EAAE,CAAC,CAAC,CAAC;YACR,CAAC;YAED,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAEzB,OAAO,SAAS,CAAC,OAAO,CAAC;QAC3B,CAAC;QAED,MAAM,IAAI,GAAY;YACpB,KAAK;YAEL,sEAAsE;YACtE,IAAI,CACF,WAAqE,EACrE,UAAuE;gBAEvE,OAAO,iBAAiB,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;YAC3D,CAAC;YAED,2DAA2D;YAC3D,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;gBAC3B,OAAO,IAAI,EAAE,CAAC;oBACZ,MAAM,MAAM,IAAI,CAAC;gBACnB,CAAC;YACH,CAAC;SACF,CAAC;QAEF,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { StepMetadata } from '../step/get-step-metadata.js';
2
+ export { FatalError, RetryableError, type RetryableErrorOptions, } from '@workflow/errors';
3
+ export type { Hook, HookOptions } from '../create-hook.js';
4
+ export { createHook, createWebhook } from './create-hook.js';
5
+ export { defineHook } from './define-hook.js';
6
+ export { getWorkflowMetadata } from './get-workflow-metadata.js';
7
+ export { getWritable } from './writable-stream.js';
8
+ export declare function getStepMetadata(): StepMetadata;
9
+ export declare function resumeHook(): void;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workflow/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EACL,UAAU,EACV,cAAc,EACd,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAInD,wBAAgB,eAAe,IAAI,YAAY,CAI9C;AACD,wBAAgB,UAAU,SAIzB"}
@@ -0,0 +1,14 @@
1
+ export { FatalError, RetryableError, } from '@workflow/errors';
2
+ export { createHook, createWebhook } from './create-hook.js';
3
+ export { defineHook } from './define-hook.js';
4
+ export { getWorkflowMetadata } from './get-workflow-metadata.js';
5
+ export { getWritable } from './writable-stream.js';
6
+ // workflows can't use these functions, but we still need to provide
7
+ // the export so bundling doesn't fail when step and workflow are in same file
8
+ export function getStepMetadata() {
9
+ throw new Error('`getStepMetadata()` can only be called inside a step function');
10
+ }
11
+ export function resumeHook() {
12
+ throw new Error('`resumeHook()` can only be called from outside a workflow function');
13
+ }
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workflow/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,cAAc,GAEf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,oEAAoE;AACpE,8EAA8E;AAC9E,MAAM,UAAU,eAAe;IAC7B,MAAM,IAAI,KAAK,CACb,+DAA+D,CAChE,CAAC;AACJ,CAAC;AACD,MAAM,UAAU,UAAU;IACxB,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { WorkflowWritableStreamOptions } from '../writable-stream.js';
2
+ export declare function getWritable<W = any>(options?: WorkflowWritableStreamOptions): WritableStream<W>;
3
+ //# sourceMappingURL=writable-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writable-stream.d.ts","sourceRoot":"","sources":["../../src/workflow/writable-stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAE3E,wBAAgB,WAAW,CAAC,CAAC,GAAG,GAAG,EACjC,OAAO,GAAE,6BAAkC,GAC1C,cAAc,CAAC,CAAC,CAAC,CASnB"}
@@ -0,0 +1,12 @@
1
+ import { STREAM_NAME_SYMBOL, WORKFLOW_GET_STREAM_ID } from '../symbols.js';
2
+ export function getWritable(options = {}) {
3
+ const { namespace } = options;
4
+ const name = globalThis[WORKFLOW_GET_STREAM_ID](namespace);
5
+ return Object.create(globalThis.WritableStream.prototype, {
6
+ [STREAM_NAME_SYMBOL]: {
7
+ value: name,
8
+ writable: false,
9
+ },
10
+ });
11
+ }
12
+ //# sourceMappingURL=writable-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writable-stream.js","sourceRoot":"","sources":["../../src/workflow/writable-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAG3E,MAAM,UAAU,WAAW,CACzB,UAAyC,EAAE;IAE3C,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAC9B,MAAM,IAAI,GAAI,UAAkB,CAAC,sBAAsB,CAAC,CAAC,SAAS,CAAC,CAAC;IACpE,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,SAAS,EAAE;QACxD,CAAC,kBAAkB,CAAC,EAAE;YACpB,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,KAAK;SAChB;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Event, WorkflowRun } from '@workflow/world';
2
+ export declare function runWorkflow(workflowCode: string, workflowRun: WorkflowRun, events: Event[]): Promise<unknown>;
3
+ //# sourceMappingURL=workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAyB1D,wBAAsB,WAAW,CAC/B,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,KAAK,EAAE,GACd,OAAO,CAAC,OAAO,CAAC,CAuhBlB"}
@@ -0,0 +1,454 @@
1
+ import { runInContext } from 'node:vm';
2
+ import { ERROR_SLUGS } from '@workflow/errors';
3
+ import * as nanoid from 'nanoid';
4
+ import { monotonicFactory } from 'ulid';
5
+ import { EventConsumerResult, EventsConsumer } from './events-consumer.js';
6
+ import { ENOTSUP } from './global.js';
7
+ import { dehydrateWorkflowReturnValue, hydrateWorkflowArguments, } from './serialization.js';
8
+ import { createUseStep } from './step.js';
9
+ import { BODY_INIT_SYMBOL, WORKFLOW_CREATE_HOOK, WORKFLOW_GET_STREAM_ID, WORKFLOW_USE_STEP, } from './symbols.js';
10
+ import * as Attribute from './telemetry/semantic-conventions.js';
11
+ import { trace } from './telemetry.js';
12
+ import { getWorkflowRunStreamId, withResolvers } from './util.js';
13
+ import { createContext } from './vm/index.js';
14
+ import { WORKFLOW_CONTEXT_SYMBOL } from './workflow/get-workflow-metadata.js';
15
+ import { createCreateHook } from './workflow/hook.js';
16
+ export async function runWorkflow(workflowCode, workflowRun, events) {
17
+ return trace(`WORKFLOW.run ${workflowRun.workflowName}`, async (span) => {
18
+ span?.setAttributes({
19
+ ...Attribute.WorkflowName(workflowRun.workflowName),
20
+ ...Attribute.WorkflowRunId(workflowRun.runId),
21
+ ...Attribute.WorkflowRunStatus(workflowRun.status),
22
+ ...Attribute.WorkflowEventsCount(events.length),
23
+ });
24
+ const startedAt = workflowRun.startedAt;
25
+ if (!startedAt) {
26
+ throw new Error(`Workflow run "${workflowRun.runId}" has no "startedAt" timestamp (should not happen)`);
27
+ }
28
+ const { context, globalThis: vmGlobalThis, updateTimestamp, } = createContext({
29
+ seed: workflowRun.runId,
30
+ fixedTimestamp: +startedAt,
31
+ });
32
+ const workflowDiscontinuation = withResolvers();
33
+ const ulid = monotonicFactory(() => vmGlobalThis.Math.random());
34
+ const generateNanoid = nanoid.customRandom(nanoid.urlAlphabet, 21, (size) => new Uint8Array(size).map(() => 256 * vmGlobalThis.Math.random()));
35
+ const workflowContext = {
36
+ globalThis: vmGlobalThis,
37
+ onWorkflowError: workflowDiscontinuation.reject,
38
+ eventsConsumer: new EventsConsumer(events),
39
+ generateUlid: () => ulid(+startedAt),
40
+ generateNanoid,
41
+ invocationsQueue: [],
42
+ };
43
+ // Subscribe to the events log to update the timestamp in the vm context
44
+ workflowContext.eventsConsumer.subscribe((event) => {
45
+ const createdAt = event?.createdAt;
46
+ if (createdAt) {
47
+ updateTimestamp(+createdAt);
48
+ }
49
+ // Never consume events - this is only a passive subscriber
50
+ return EventConsumerResult.NotConsumed;
51
+ });
52
+ const useStep = createUseStep(workflowContext);
53
+ const createHook = createCreateHook(workflowContext);
54
+ // @ts-expect-error - `@types/node` says symbol is not valid, but it does work
55
+ vmGlobalThis[WORKFLOW_USE_STEP] = useStep;
56
+ // @ts-expect-error - `@types/node` says symbol is not valid, but it does work
57
+ vmGlobalThis[WORKFLOW_CREATE_HOOK] = createHook;
58
+ // @ts-expect-error - `@types/node` says symbol is not valid, but it does work
59
+ vmGlobalThis[WORKFLOW_GET_STREAM_ID] = (namespace) => getWorkflowRunStreamId(workflowRun.runId, namespace);
60
+ // TODO: there should be a getUrl method on the world interface itself. This
61
+ // solution only works for vercel + embedded worlds.
62
+ const url = process.env.VERCEL_URL
63
+ ? `https://${process.env.VERCEL_URL}`
64
+ : `http://localhost:${process.env.PORT || 3000}`;
65
+ // For the workflow VM, we store the context in a symbol on the `globalThis` object
66
+ const ctx = {
67
+ workflowRunId: workflowRun.runId,
68
+ workflowStartedAt: new vmGlobalThis.Date(+startedAt),
69
+ url,
70
+ };
71
+ // @ts-expect-error - `@types/node` says symbol is not valid, but it does work
72
+ vmGlobalThis[WORKFLOW_CONTEXT_SYMBOL] = ctx;
73
+ // NOTE: Will have a config override to use the custom fetch step.
74
+ // For now `fetch` must be explicitly imported from `workflow`.
75
+ vmGlobalThis.fetch = () => {
76
+ throw new vmGlobalThis.Error(`Global "fetch" is unavailable in workflow functions. Use the "fetch" step function from "workflow" to make HTTP requests.\n\nLearn more: https://useworkflow.dev/err/${ERROR_SLUGS.FETCH_IN_WORKFLOW_FUNCTION}`);
77
+ };
78
+ // `Request` and `Response` are special built-in classes that invoke steps
79
+ // for the `json()`, `text()` and `arrayBuffer()` instance methods
80
+ class Request {
81
+ cache;
82
+ credentials;
83
+ destination;
84
+ headers;
85
+ integrity;
86
+ method;
87
+ mode;
88
+ redirect;
89
+ referrer;
90
+ referrerPolicy;
91
+ url;
92
+ keepalive;
93
+ signal;
94
+ duplex;
95
+ body;
96
+ constructor(input, init) {
97
+ // Handle URL input
98
+ if (typeof input === 'string' || input instanceof vmGlobalThis.URL) {
99
+ const urlString = String(input);
100
+ // Validate URL format
101
+ try {
102
+ new vmGlobalThis.URL(urlString);
103
+ this.url = urlString;
104
+ }
105
+ catch (cause) {
106
+ throw new TypeError(`Failed to parse URL from ${urlString}`, {
107
+ cause,
108
+ });
109
+ }
110
+ }
111
+ else {
112
+ // Input is a Request object - clone its properties
113
+ this.url = input.url;
114
+ if (!init) {
115
+ this.method = input.method;
116
+ this.headers = new vmGlobalThis.Headers(input.headers);
117
+ this.body = input.body;
118
+ this.mode = input.mode;
119
+ this.credentials = input.credentials;
120
+ this.cache = input.cache;
121
+ this.redirect = input.redirect;
122
+ this.referrer = input.referrer;
123
+ this.referrerPolicy = input.referrerPolicy;
124
+ this.integrity = input.integrity;
125
+ this.keepalive = input.keepalive;
126
+ this.signal = input.signal;
127
+ this.duplex = input.duplex;
128
+ this.destination = input.destination;
129
+ return;
130
+ }
131
+ // If init is provided, merge: use source properties, then override with init
132
+ // Copy all properties from the source Request first
133
+ this.method = input.method;
134
+ this.headers = new vmGlobalThis.Headers(input.headers);
135
+ this.body = input.body;
136
+ this.mode = input.mode;
137
+ this.credentials = input.credentials;
138
+ this.cache = input.cache;
139
+ this.redirect = input.redirect;
140
+ this.referrer = input.referrer;
141
+ this.referrerPolicy = input.referrerPolicy;
142
+ this.integrity = input.integrity;
143
+ this.keepalive = input.keepalive;
144
+ this.signal = input.signal;
145
+ this.duplex = input.duplex;
146
+ this.destination = input.destination;
147
+ }
148
+ // Override with init options if provided
149
+ // Set method
150
+ if (init?.method) {
151
+ this.method = init.method.toUpperCase();
152
+ }
153
+ else if (typeof this.method !== 'string') {
154
+ // Fallback to default for string input case
155
+ this.method = 'GET';
156
+ }
157
+ // Set headers
158
+ if (init?.headers) {
159
+ this.headers = new vmGlobalThis.Headers(init.headers);
160
+ }
161
+ else if (typeof input === 'string' ||
162
+ input instanceof vmGlobalThis.URL) {
163
+ // For string/URL input, create empty headers
164
+ this.headers = new vmGlobalThis.Headers();
165
+ }
166
+ // Set other properties with init values or defaults
167
+ if (init?.mode !== undefined) {
168
+ this.mode = init.mode;
169
+ }
170
+ else if (typeof this.mode !== 'string') {
171
+ this.mode = 'cors';
172
+ }
173
+ if (init?.credentials !== undefined) {
174
+ this.credentials = init.credentials;
175
+ }
176
+ else if (typeof this.credentials !== 'string') {
177
+ this.credentials = 'same-origin';
178
+ }
179
+ if (init?.cache !== undefined) {
180
+ this.cache = init.cache;
181
+ }
182
+ else if (typeof this.cache !== 'string') {
183
+ this.cache = 'default';
184
+ }
185
+ if (init?.redirect !== undefined) {
186
+ this.redirect = init.redirect;
187
+ }
188
+ else if (typeof this.redirect !== 'string') {
189
+ this.redirect = 'follow';
190
+ }
191
+ if (init?.referrer !== undefined) {
192
+ this.referrer = init.referrer;
193
+ }
194
+ else if (typeof this.referrer !== 'string') {
195
+ this.referrer = 'about:client';
196
+ }
197
+ if (init?.referrerPolicy !== undefined) {
198
+ this.referrerPolicy = init.referrerPolicy;
199
+ }
200
+ else if (typeof this.referrerPolicy !== 'string') {
201
+ this.referrerPolicy = '';
202
+ }
203
+ if (init?.integrity !== undefined) {
204
+ this.integrity = init.integrity;
205
+ }
206
+ else if (typeof this.integrity !== 'string') {
207
+ this.integrity = '';
208
+ }
209
+ if (init?.keepalive !== undefined) {
210
+ this.keepalive = init.keepalive;
211
+ }
212
+ else if (typeof this.keepalive !== 'boolean') {
213
+ this.keepalive = false;
214
+ }
215
+ if (init?.signal !== undefined) {
216
+ // @ts-expect-error - AbortSignal stub
217
+ this.signal = init.signal;
218
+ }
219
+ else if (!this.signal) {
220
+ // @ts-expect-error - AbortSignal stub
221
+ this.signal = { aborted: false };
222
+ }
223
+ if (!this.duplex) {
224
+ this.duplex = 'half';
225
+ }
226
+ if (!this.destination) {
227
+ this.destination = 'document';
228
+ }
229
+ const body = init?.body;
230
+ // Validate that GET/HEAD methods don't have a body
231
+ if (body !== null &&
232
+ body !== undefined &&
233
+ (this.method === 'GET' || this.method === 'HEAD')) {
234
+ throw new TypeError(`Request with GET/HEAD method cannot have body.`);
235
+ }
236
+ // Store the original BodyInit for serialization
237
+ if (body !== null && body !== undefined) {
238
+ // Create a "fake" ReadableStream that stores the original body
239
+ // This avoids doing async work during workflow replay
240
+ this.body = Object.create(vmGlobalThis.ReadableStream.prototype, {
241
+ [BODY_INIT_SYMBOL]: {
242
+ value: body,
243
+ writable: false,
244
+ },
245
+ });
246
+ }
247
+ else {
248
+ this.body = null;
249
+ }
250
+ }
251
+ clone() {
252
+ ENOTSUP();
253
+ }
254
+ get bodyUsed() {
255
+ return false;
256
+ }
257
+ // TODO: implement these
258
+ blob;
259
+ formData;
260
+ async arrayBuffer() {
261
+ return resArrayBuffer(this);
262
+ }
263
+ async bytes() {
264
+ return new Uint8Array(await resArrayBuffer(this));
265
+ }
266
+ async json() {
267
+ return resJson(this);
268
+ }
269
+ async text() {
270
+ return resText(this);
271
+ }
272
+ }
273
+ vmGlobalThis.Request = Request;
274
+ const resJson = useStep('__builtin_response_json');
275
+ const resText = useStep('__builtin_response_text');
276
+ const resArrayBuffer = useStep('__builtin_response_array_buffer');
277
+ class Response {
278
+ type;
279
+ url;
280
+ status;
281
+ statusText;
282
+ body;
283
+ headers;
284
+ redirected;
285
+ constructor(body, init) {
286
+ this.status = init?.status ?? 200;
287
+ this.statusText = init?.statusText ?? '';
288
+ this.headers = new vmGlobalThis.Headers(init?.headers);
289
+ this.type = 'default';
290
+ this.url = '';
291
+ this.redirected = false;
292
+ // Validate that null-body status codes don't have a body
293
+ // Per HTTP spec: 204 (No Content), 205 (Reset Content), and 304 (Not Modified)
294
+ if (body !== null &&
295
+ body !== undefined &&
296
+ (this.status === 204 || this.status === 205 || this.status === 304)) {
297
+ throw new TypeError(`Response constructor: Invalid response status code ${this.status}`);
298
+ }
299
+ // Store the original BodyInit for serialization
300
+ if (body !== null && body !== undefined) {
301
+ // Create a "fake" ReadableStream that stores the original body
302
+ // This avoids doing async work during workflow replay
303
+ this.body = Object.create(vmGlobalThis.ReadableStream.prototype, {
304
+ [BODY_INIT_SYMBOL]: {
305
+ value: body,
306
+ writable: false,
307
+ },
308
+ });
309
+ }
310
+ else {
311
+ this.body = null;
312
+ }
313
+ }
314
+ // TODO: implement these
315
+ clone;
316
+ blob;
317
+ formData;
318
+ get ok() {
319
+ return this.status >= 200 && this.status < 300;
320
+ }
321
+ get bodyUsed() {
322
+ return false;
323
+ }
324
+ async arrayBuffer() {
325
+ return resArrayBuffer(this);
326
+ }
327
+ async bytes() {
328
+ return new Uint8Array(await resArrayBuffer(this));
329
+ }
330
+ async json() {
331
+ return resJson(this);
332
+ }
333
+ static json(data, init) {
334
+ const body = JSON.stringify(data);
335
+ const headers = new vmGlobalThis.Headers(init?.headers);
336
+ if (!headers.has('content-type')) {
337
+ headers.set('content-type', 'application/json');
338
+ }
339
+ return new Response(body, { ...init, headers });
340
+ }
341
+ async text() {
342
+ return resText(this);
343
+ }
344
+ static error() {
345
+ ENOTSUP();
346
+ }
347
+ static redirect(url, status = 302) {
348
+ // Validate status code - only specific redirect codes are allowed
349
+ if (![301, 302, 303, 307, 308].includes(status)) {
350
+ throw new RangeError(`Invalid redirect status code: ${status}. Must be one of: 301, 302, 303, 307, 308`);
351
+ }
352
+ // Create response with Location header
353
+ const headers = new vmGlobalThis.Headers();
354
+ headers.set('Location', String(url));
355
+ const response = Object.create(Response.prototype);
356
+ response.status = status;
357
+ response.statusText = '';
358
+ response.headers = headers;
359
+ response.body = null;
360
+ response.type = 'default';
361
+ response.url = '';
362
+ response.redirected = false;
363
+ return response;
364
+ }
365
+ }
366
+ vmGlobalThis.Response = Response;
367
+ class ReadableStream {
368
+ constructor() {
369
+ ENOTSUP();
370
+ }
371
+ get locked() {
372
+ return false;
373
+ }
374
+ cancel() {
375
+ ENOTSUP();
376
+ }
377
+ getReader() {
378
+ ENOTSUP();
379
+ }
380
+ pipeThrough() {
381
+ ENOTSUP();
382
+ }
383
+ pipeTo() {
384
+ ENOTSUP();
385
+ }
386
+ tee() {
387
+ ENOTSUP();
388
+ }
389
+ values() {
390
+ ENOTSUP();
391
+ }
392
+ static from() {
393
+ ENOTSUP();
394
+ }
395
+ [Symbol.asyncIterator]() {
396
+ ENOTSUP();
397
+ }
398
+ }
399
+ vmGlobalThis.ReadableStream = ReadableStream;
400
+ class WritableStream {
401
+ constructor() {
402
+ ENOTSUP();
403
+ }
404
+ get locked() {
405
+ return false;
406
+ }
407
+ abort() {
408
+ ENOTSUP();
409
+ }
410
+ close() {
411
+ ENOTSUP();
412
+ }
413
+ getWriter() {
414
+ ENOTSUP();
415
+ }
416
+ }
417
+ vmGlobalThis.WritableStream = WritableStream;
418
+ class TransformStream {
419
+ readable;
420
+ writable;
421
+ constructor() {
422
+ ENOTSUP();
423
+ }
424
+ }
425
+ vmGlobalThis.TransformStream = TransformStream;
426
+ // Eventually we'll probably want to provide our own `console` object,
427
+ // but for now we'll just expose the global one.
428
+ vmGlobalThis.console = globalThis.console;
429
+ // HACK: propagate symbol needed for AI gateway usage
430
+ const SYMBOL_FOR_REQ_CONTEXT = Symbol.for('@vercel/request-context');
431
+ // @ts-expect-error - `@types/node` says symbol is not valid, but it does work
432
+ vmGlobalThis[SYMBOL_FOR_REQ_CONTEXT] = globalThis[SYMBOL_FOR_REQ_CONTEXT];
433
+ // Get a reference to the user-defined workflow function
434
+ const workflowFn = runInContext(`${workflowCode}; globalThis.__private_workflows?.get(${JSON.stringify(workflowRun.workflowName)})`, context);
435
+ if (typeof workflowFn !== 'function') {
436
+ throw new ReferenceError(`Workflow ${JSON.stringify(workflowRun.workflowName)} must be a function, but got "${typeof workflowFn}" instead`);
437
+ }
438
+ const args = hydrateWorkflowArguments(workflowRun.input, vmGlobalThis);
439
+ span?.setAttributes({
440
+ ...Attribute.WorkflowArgumentsCount(args.length),
441
+ });
442
+ // Invoke user workflow
443
+ const result = await Promise.race([
444
+ workflowFn(...args),
445
+ workflowDiscontinuation.promise,
446
+ ]);
447
+ const dehydrated = dehydrateWorkflowReturnValue(result, vmGlobalThis);
448
+ span?.setAttributes({
449
+ ...Attribute.WorkflowResultType(typeof result),
450
+ });
451
+ return dehydrated;
452
+ });
453
+ }
454
+ //# sourceMappingURL=workflow.js.map