@dudousxd/nestjs-durable 0.39.1 → 0.41.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.cjs CHANGED
@@ -82,9 +82,120 @@ var import_nestjs_durable_core2 = require("@dudousxd/nestjs-durable-core");
82
82
  // src/decorators.ts
83
83
  var import_nestjs_durable_core = require("@dudousxd/nestjs-durable-core");
84
84
  var import_reflect_metadata = require("reflect-metadata");
85
+
86
+ // src/origin.ts
87
+ var import_node_fs = require("fs");
88
+ var import_node_path = require("path");
89
+ var import_node_url = require("url");
90
+ var declaringFileByClass = /* @__PURE__ */ new WeakMap();
91
+ var packageNameByDir = /* @__PURE__ */ new Map();
92
+ function declaringFile(boundary) {
93
+ const capture = Error.captureStackTrace;
94
+ if (typeof capture !== "function") return void 0;
95
+ const holder = {};
96
+ capture(holder, boundary);
97
+ return topFrameFile(holder.stack);
98
+ }
99
+ __name(declaringFile, "declaringFile");
100
+ function rememberDeclaringFile(target, file) {
101
+ if (file) declaringFileByClass.set(target, file);
102
+ }
103
+ __name(rememberDeclaringFile, "rememberDeclaringFile");
104
+ function originOfClass(target) {
105
+ for (let ctor = target; typeof ctor === "function" || typeof ctor === "object" && ctor !== null; ctor = Object.getPrototypeOf(ctor)) {
106
+ const file = declaringFileByClass.get(ctor);
107
+ if (file) return packageNameFor(file);
108
+ }
109
+ return void 0;
110
+ }
111
+ __name(originOfClass, "originOfClass");
112
+ function originOfProvider(wrapper) {
113
+ const fromMetatype = originOfClass(wrapper.metatype);
114
+ if (fromMetatype) return fromMetatype;
115
+ const instance = wrapper.instance;
116
+ if (typeof instance !== "object" || instance === null) return void 0;
117
+ return originOfClass(instance.constructor);
118
+ }
119
+ __name(originOfProvider, "originOfProvider");
120
+ function packageNameFor(file) {
121
+ const start = (0, import_node_path.dirname)(file);
122
+ const visited = [];
123
+ let dir = start;
124
+ for (; ; ) {
125
+ if (packageNameByDir.has(dir)) {
126
+ const cached = packageNameByDir.get(dir);
127
+ for (const seen of visited) packageNameByDir.set(seen, cached);
128
+ return cached;
129
+ }
130
+ visited.push(dir);
131
+ const name = readPackageName((0, import_node_path.join)(dir, "package.json"));
132
+ if (name !== void 0) {
133
+ for (const seen of visited) packageNameByDir.set(seen, name);
134
+ return name;
135
+ }
136
+ const parent = (0, import_node_path.dirname)(dir);
137
+ if (parent === dir) {
138
+ for (const seen of visited) packageNameByDir.set(seen, void 0);
139
+ return void 0;
140
+ }
141
+ dir = parent;
142
+ }
143
+ }
144
+ __name(packageNameFor, "packageNameFor");
145
+ function readPackageName(path) {
146
+ let raw;
147
+ try {
148
+ raw = (0, import_node_fs.readFileSync)(path, "utf8");
149
+ } catch {
150
+ return void 0;
151
+ }
152
+ let parsed;
153
+ try {
154
+ parsed = JSON.parse(raw);
155
+ } catch {
156
+ return void 0;
157
+ }
158
+ if (!isRecord(parsed)) return void 0;
159
+ const name = parsed.name;
160
+ return typeof name === "string" && name.length > 0 ? name : void 0;
161
+ }
162
+ __name(readPackageName, "readPackageName");
163
+ function isRecord(value) {
164
+ return typeof value === "object" && value !== null;
165
+ }
166
+ __name(isRecord, "isRecord");
167
+ function topFrameFile(stack) {
168
+ if (typeof stack !== "string") return void 0;
169
+ for (const line of stack.split("\n")) {
170
+ const frame = line.trim();
171
+ if (!frame.startsWith("at ")) continue;
172
+ return fileOfFrame(frame.slice(3));
173
+ }
174
+ return void 0;
175
+ }
176
+ __name(topFrameFile, "topFrameFile");
177
+ function fileOfFrame(frame) {
178
+ const match = /\((.+):\d+:\d+\)$/.exec(frame) ?? /^(.+):\d+:\d+$/.exec(frame);
179
+ const location = match?.[1];
180
+ if (location === void 0) return void 0;
181
+ if (location.startsWith("file://")) {
182
+ try {
183
+ return (0, import_node_url.fileURLToPath)(location);
184
+ } catch {
185
+ return void 0;
186
+ }
187
+ }
188
+ const absolute = location.startsWith("/") || /^[A-Za-z]:[\\/]/.test(location);
189
+ return absolute ? location : void 0;
190
+ }
191
+ __name(fileOfFrame, "fileOfFrame");
192
+
193
+ // src/decorators.ts
85
194
  var WORKFLOW_METADATA = Symbol("nestjs-durable:workflow");
86
195
  function Workflow(options) {
196
+ const declaredIn = declaringFile(Workflow);
87
197
  return (target) => {
198
+ rememberDeclaringFile(target, declaredIn);
88
199
  const meta = {
89
200
  name: options.name,
90
201
  version: options.version ?? "1",
@@ -313,7 +424,7 @@ function scanWorkflows(discovery, register) {
313
424
  if (typeof workflow.run !== "function") {
314
425
  throw new Error(`@Workflow ${meta.name} must define a run(ctx, input) method`);
315
426
  }
316
- register(meta, workflow);
427
+ register(meta, workflow, originOfProvider(wrapper));
317
428
  }
318
429
  }
319
430
  __name(scanWorkflows, "scanWorkflows");
@@ -377,7 +488,15 @@ var ThinWorkflowRegistrar = class {
377
488
  }
378
489
  onModuleInit() {
379
490
  if (!isPureThinWorker(this.options)) return;
380
- scanWorkflows(this.discovery, (meta, instance) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)));
491
+ scanWorkflows(this.discovery, (meta, instance, origin) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
492
+ version: meta.version,
493
+ ...meta.requires !== void 0 ? {
494
+ requires: meta.requires
495
+ } : {},
496
+ ...origin !== void 0 ? {
497
+ origin
498
+ } : {}
499
+ }));
381
500
  }
382
501
  };
383
502
  ThinWorkflowRegistrar = _ts_decorate2([
@@ -771,7 +890,15 @@ var InAppWorkerBootstrap = class {
771
890
  }
772
891
  onModuleInit() {
773
892
  if (!isCoLocatedWorker(this.options)) return;
774
- scanWorkflows(this.discovery, (meta, instance) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)));
893
+ scanWorkflows(this.discovery, (meta, instance, origin) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
894
+ version: meta.version,
895
+ ...meta.requires !== void 0 ? {
896
+ requires: meta.requires
897
+ } : {},
898
+ ...origin !== void 0 ? {
899
+ origin
900
+ } : {}
901
+ }));
775
902
  scanSteps(this.discovery, this.metadataScanner, (meta, handler) => this.runtime.registerStep(meta.name, handler));
776
903
  }
777
904
  async onApplicationBootstrap() {
@@ -1610,7 +1737,9 @@ var WorkflowRegistrar = class {
1610
1737
  this.engine.registerEntity(entityMeta.name, entityConfigFor(instance.constructor));
1611
1738
  }
1612
1739
  }
1613
- scanWorkflows(this.discovery, (meta, workflow) => {
1740
+ const unattributed = [];
1741
+ scanWorkflows(this.discovery, (meta, workflow, origin) => {
1742
+ if (origin === void 0) unattributed.push(meta.name);
1614
1743
  const validateInput = meta.validateInput ?? (meta.inputSchema ? classValidatorInput(meta.inputSchema) : void 0);
1615
1744
  const eventBatch = meta.debounce ? {
1616
1745
  mode: "debounce",
@@ -1622,6 +1751,10 @@ var WorkflowRegistrar = class {
1622
1751
  } : void 0;
1623
1752
  const workflowCtor = workflow.constructor;
1624
1753
  this.engine.register(meta.name, meta.version, (ctx, input) => workflow.run(ctx, input), {
1754
+ // Stamped onto every run this workflow starts (see `WorkflowRun.origin`). Derived from the
1755
+ // class's declaring file, so a lib gets attributed without opting in; `undefined` when it
1756
+ // could not be derived, which the engine records as unknown rather than defaulting.
1757
+ origin,
1625
1758
  tags: meta.tags,
1626
1759
  singleton: meta.singleton,
1627
1760
  executionTimeout: meta.executionTimeout,
@@ -1654,14 +1787,27 @@ var WorkflowRegistrar = class {
1654
1787
  }
1655
1788
  if (inline) {
1656
1789
  const dlqName = `${meta.name}.dlq`;
1657
- this.engine.register(dlqName, meta.version, inline);
1790
+ this.engine.register(dlqName, meta.version, inline, {
1791
+ origin
1792
+ });
1658
1793
  deadLetterByWorkflow.set(meta.name, dlqName);
1659
1794
  } else if (meta.deadLetterWorkflow) {
1660
1795
  deadLetterByWorkflow.set(meta.name, (0, import_nestjs_durable_core12.workflowName)(meta.deadLetterWorkflow));
1661
1796
  }
1662
1797
  });
1798
+ this.reportUnattributed(unattributed);
1663
1799
  this.installDeadLetterRouting(deadLetterByWorkflow);
1664
1800
  }
1801
+ /**
1802
+ * Says, once at boot, which workflows could not be attributed to a package — their runs are stamped
1803
+ * with no `origin` at all, so an operator filtering the dashboard by origin will not see them. That
1804
+ * is the deliberate trade (an absent origin over an invented one), but it has to be visible: a
1805
+ * silently-dropped run is indistinguishable from a run that never happened.
1806
+ */
1807
+ reportUnattributed(names) {
1808
+ if (names.length === 0) return;
1809
+ 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.`);
1810
+ }
1665
1811
  /** Returns the instance's `@DeadLetter()` method bound to the instance, or undefined if none. */
1666
1812
  findDeadLetterHandler(instance) {
1667
1813
  const prototype = Object.getPrototypeOf(instance);