@dudousxd/nestjs-durable 0.39.0 → 0.40.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.
package/dist/index.js CHANGED
@@ -13,9 +13,120 @@ import { readSearchAttributes } from "@dudousxd/nestjs-durable-core";
13
13
  // src/decorators.ts
14
14
  import { DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, WORKFLOW_NAME_KEY } from "@dudousxd/nestjs-durable-core";
15
15
  import "reflect-metadata";
16
+
17
+ // src/origin.ts
18
+ import { readFileSync } from "node:fs";
19
+ import { dirname, join } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+ var declaringFileByClass = /* @__PURE__ */ new WeakMap();
22
+ var packageNameByDir = /* @__PURE__ */ new Map();
23
+ function declaringFile(boundary) {
24
+ const capture = Error.captureStackTrace;
25
+ if (typeof capture !== "function") return void 0;
26
+ const holder = {};
27
+ capture(holder, boundary);
28
+ return topFrameFile(holder.stack);
29
+ }
30
+ __name(declaringFile, "declaringFile");
31
+ function rememberDeclaringFile(target, file) {
32
+ if (file) declaringFileByClass.set(target, file);
33
+ }
34
+ __name(rememberDeclaringFile, "rememberDeclaringFile");
35
+ function originOfClass(target) {
36
+ for (let ctor = target; typeof ctor === "function" || typeof ctor === "object" && ctor !== null; ctor = Object.getPrototypeOf(ctor)) {
37
+ const file = declaringFileByClass.get(ctor);
38
+ if (file) return packageNameFor(file);
39
+ }
40
+ return void 0;
41
+ }
42
+ __name(originOfClass, "originOfClass");
43
+ function originOfProvider(wrapper) {
44
+ const fromMetatype = originOfClass(wrapper.metatype);
45
+ if (fromMetatype) return fromMetatype;
46
+ const instance = wrapper.instance;
47
+ if (typeof instance !== "object" || instance === null) return void 0;
48
+ return originOfClass(instance.constructor);
49
+ }
50
+ __name(originOfProvider, "originOfProvider");
51
+ function packageNameFor(file) {
52
+ const start = dirname(file);
53
+ const visited = [];
54
+ let dir = start;
55
+ for (; ; ) {
56
+ if (packageNameByDir.has(dir)) {
57
+ const cached = packageNameByDir.get(dir);
58
+ for (const seen of visited) packageNameByDir.set(seen, cached);
59
+ return cached;
60
+ }
61
+ visited.push(dir);
62
+ const name = readPackageName(join(dir, "package.json"));
63
+ if (name !== void 0) {
64
+ for (const seen of visited) packageNameByDir.set(seen, name);
65
+ return name;
66
+ }
67
+ const parent = dirname(dir);
68
+ if (parent === dir) {
69
+ for (const seen of visited) packageNameByDir.set(seen, void 0);
70
+ return void 0;
71
+ }
72
+ dir = parent;
73
+ }
74
+ }
75
+ __name(packageNameFor, "packageNameFor");
76
+ function readPackageName(path) {
77
+ let raw;
78
+ try {
79
+ raw = readFileSync(path, "utf8");
80
+ } catch {
81
+ return void 0;
82
+ }
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(raw);
86
+ } catch {
87
+ return void 0;
88
+ }
89
+ if (!isRecord(parsed)) return void 0;
90
+ const name = parsed.name;
91
+ return typeof name === "string" && name.length > 0 ? name : void 0;
92
+ }
93
+ __name(readPackageName, "readPackageName");
94
+ function isRecord(value) {
95
+ return typeof value === "object" && value !== null;
96
+ }
97
+ __name(isRecord, "isRecord");
98
+ function topFrameFile(stack) {
99
+ if (typeof stack !== "string") return void 0;
100
+ for (const line of stack.split("\n")) {
101
+ const frame = line.trim();
102
+ if (!frame.startsWith("at ")) continue;
103
+ return fileOfFrame(frame.slice(3));
104
+ }
105
+ return void 0;
106
+ }
107
+ __name(topFrameFile, "topFrameFile");
108
+ function fileOfFrame(frame) {
109
+ const match = /\((.+):\d+:\d+\)$/.exec(frame) ?? /^(.+):\d+:\d+$/.exec(frame);
110
+ const location = match?.[1];
111
+ if (location === void 0) return void 0;
112
+ if (location.startsWith("file://")) {
113
+ try {
114
+ return fileURLToPath(location);
115
+ } catch {
116
+ return void 0;
117
+ }
118
+ }
119
+ const absolute = location.startsWith("/") || /^[A-Za-z]:[\\/]/.test(location);
120
+ return absolute ? location : void 0;
121
+ }
122
+ __name(fileOfFrame, "fileOfFrame");
123
+
124
+ // src/decorators.ts
16
125
  var WORKFLOW_METADATA = Symbol("nestjs-durable:workflow");
17
126
  function Workflow(options) {
127
+ const declaredIn = declaringFile(Workflow);
18
128
  return (target) => {
129
+ rememberDeclaringFile(target, declaredIn);
19
130
  const meta = {
20
131
  name: options.name,
21
132
  version: options.version ?? "1",
@@ -244,7 +355,7 @@ function scanWorkflows(discovery, register) {
244
355
  if (typeof workflow.run !== "function") {
245
356
  throw new Error(`@Workflow ${meta.name} must define a run(ctx, input) method`);
246
357
  }
247
- register(meta, workflow);
358
+ register(meta, workflow, originOfProvider(wrapper));
248
359
  }
249
360
  }
250
361
  __name(scanWorkflows, "scanWorkflows");
@@ -1541,7 +1652,9 @@ var WorkflowRegistrar = class {
1541
1652
  this.engine.registerEntity(entityMeta.name, entityConfigFor(instance.constructor));
1542
1653
  }
1543
1654
  }
1544
- scanWorkflows(this.discovery, (meta, workflow) => {
1655
+ const unattributed = [];
1656
+ scanWorkflows(this.discovery, (meta, workflow, origin) => {
1657
+ if (origin === void 0) unattributed.push(meta.name);
1545
1658
  const validateInput = meta.validateInput ?? (meta.inputSchema ? classValidatorInput(meta.inputSchema) : void 0);
1546
1659
  const eventBatch = meta.debounce ? {
1547
1660
  mode: "debounce",
@@ -1553,6 +1666,10 @@ var WorkflowRegistrar = class {
1553
1666
  } : void 0;
1554
1667
  const workflowCtor = workflow.constructor;
1555
1668
  this.engine.register(meta.name, meta.version, (ctx, input) => workflow.run(ctx, input), {
1669
+ // Stamped onto every run this workflow starts (see `WorkflowRun.origin`). Derived from the
1670
+ // class's declaring file, so a lib gets attributed without opting in; `undefined` when it
1671
+ // could not be derived, which the engine records as unknown rather than defaulting.
1672
+ origin,
1556
1673
  tags: meta.tags,
1557
1674
  singleton: meta.singleton,
1558
1675
  executionTimeout: meta.executionTimeout,
@@ -1585,14 +1702,27 @@ var WorkflowRegistrar = class {
1585
1702
  }
1586
1703
  if (inline) {
1587
1704
  const dlqName = `${meta.name}.dlq`;
1588
- this.engine.register(dlqName, meta.version, inline);
1705
+ this.engine.register(dlqName, meta.version, inline, {
1706
+ origin
1707
+ });
1589
1708
  deadLetterByWorkflow.set(meta.name, dlqName);
1590
1709
  } else if (meta.deadLetterWorkflow) {
1591
1710
  deadLetterByWorkflow.set(meta.name, workflowName2(meta.deadLetterWorkflow));
1592
1711
  }
1593
1712
  });
1713
+ this.reportUnattributed(unattributed);
1594
1714
  this.installDeadLetterRouting(deadLetterByWorkflow);
1595
1715
  }
1716
+ /**
1717
+ * Says, once at boot, which workflows could not be attributed to a package — their runs are stamped
1718
+ * with no `origin` at all, so an operator filtering the dashboard by origin will not see them. That
1719
+ * is the deliberate trade (an absent origin over an invented one), but it has to be visible: a
1720
+ * silently-dropped run is indistinguishable from a run that never happened.
1721
+ */
1722
+ reportUnattributed(names) {
1723
+ if (names.length === 0) return;
1724
+ console.warn(`[nestjs-durable] could not derive an origin (declaring package) for ${names.length} workflow(s): ${names.join(", ")}. Their runs carry no origin and will not match an origin filter.`);
1725
+ }
1596
1726
  /** Returns the instance's `@DeadLetter()` method bound to the instance, or undefined if none. */
1597
1727
  findDeadLetterHandler(instance) {
1598
1728
  const prototype = Object.getPrototypeOf(instance);
@@ -2100,7 +2230,7 @@ DurableModule = _ts_decorate12([
2100
2230
  ], DurableModule);
2101
2231
 
2102
2232
  // src/index.ts
2103
- import { readSearchAttributes as readSearchAttributes2, WorkflowEngine as WorkflowEngine7 } from "@dudousxd/nestjs-durable-core";
2233
+ import { readSearchAttributes as readSearchAttributes2, RunGateway as RunGateway3, WorkflowEngine as WorkflowEngine7 } from "@dudousxd/nestjs-durable-core";
2104
2234
  export {
2105
2235
  CONTEXT_ACCESSOR,
2106
2236
  DEAD_LETTER_METADATA,
@@ -2126,6 +2256,7 @@ export {
2126
2256
  ProxyRunGateway,
2127
2257
  RUN_GATEWAY,
2128
2258
  RUN_REDIS_WORKER,
2259
+ RunGateway3 as RunGateway,
2129
2260
  RunRequestResponder,
2130
2261
  STEP_INTERCEPTOR_METADATA,
2131
2262
  Step,