@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 +151 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +151 -5
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
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");
|
|
@@ -308,7 +419,15 @@ var ThinWorkflowRegistrar = class {
|
|
|
308
419
|
}
|
|
309
420
|
onModuleInit() {
|
|
310
421
|
if (!isPureThinWorker(this.options)) return;
|
|
311
|
-
scanWorkflows(this.discovery, (meta, instance) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)
|
|
422
|
+
scanWorkflows(this.discovery, (meta, instance, origin) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
|
|
423
|
+
version: meta.version,
|
|
424
|
+
...meta.requires !== void 0 ? {
|
|
425
|
+
requires: meta.requires
|
|
426
|
+
} : {},
|
|
427
|
+
...origin !== void 0 ? {
|
|
428
|
+
origin
|
|
429
|
+
} : {}
|
|
430
|
+
}));
|
|
312
431
|
}
|
|
313
432
|
};
|
|
314
433
|
ThinWorkflowRegistrar = _ts_decorate2([
|
|
@@ -702,7 +821,15 @@ var InAppWorkerBootstrap = class {
|
|
|
702
821
|
}
|
|
703
822
|
onModuleInit() {
|
|
704
823
|
if (!isCoLocatedWorker(this.options)) return;
|
|
705
|
-
scanWorkflows(this.discovery, (meta, instance) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)
|
|
824
|
+
scanWorkflows(this.discovery, (meta, instance, origin) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
|
|
825
|
+
version: meta.version,
|
|
826
|
+
...meta.requires !== void 0 ? {
|
|
827
|
+
requires: meta.requires
|
|
828
|
+
} : {},
|
|
829
|
+
...origin !== void 0 ? {
|
|
830
|
+
origin
|
|
831
|
+
} : {}
|
|
832
|
+
}));
|
|
706
833
|
scanSteps(this.discovery, this.metadataScanner, (meta, handler) => this.runtime.registerStep(meta.name, handler));
|
|
707
834
|
}
|
|
708
835
|
async onApplicationBootstrap() {
|
|
@@ -1541,7 +1668,9 @@ var WorkflowRegistrar = class {
|
|
|
1541
1668
|
this.engine.registerEntity(entityMeta.name, entityConfigFor(instance.constructor));
|
|
1542
1669
|
}
|
|
1543
1670
|
}
|
|
1544
|
-
|
|
1671
|
+
const unattributed = [];
|
|
1672
|
+
scanWorkflows(this.discovery, (meta, workflow, origin) => {
|
|
1673
|
+
if (origin === void 0) unattributed.push(meta.name);
|
|
1545
1674
|
const validateInput = meta.validateInput ?? (meta.inputSchema ? classValidatorInput(meta.inputSchema) : void 0);
|
|
1546
1675
|
const eventBatch = meta.debounce ? {
|
|
1547
1676
|
mode: "debounce",
|
|
@@ -1553,6 +1682,10 @@ var WorkflowRegistrar = class {
|
|
|
1553
1682
|
} : void 0;
|
|
1554
1683
|
const workflowCtor = workflow.constructor;
|
|
1555
1684
|
this.engine.register(meta.name, meta.version, (ctx, input) => workflow.run(ctx, input), {
|
|
1685
|
+
// Stamped onto every run this workflow starts (see `WorkflowRun.origin`). Derived from the
|
|
1686
|
+
// class's declaring file, so a lib gets attributed without opting in; `undefined` when it
|
|
1687
|
+
// could not be derived, which the engine records as unknown rather than defaulting.
|
|
1688
|
+
origin,
|
|
1556
1689
|
tags: meta.tags,
|
|
1557
1690
|
singleton: meta.singleton,
|
|
1558
1691
|
executionTimeout: meta.executionTimeout,
|
|
@@ -1585,14 +1718,27 @@ var WorkflowRegistrar = class {
|
|
|
1585
1718
|
}
|
|
1586
1719
|
if (inline) {
|
|
1587
1720
|
const dlqName = `${meta.name}.dlq`;
|
|
1588
|
-
this.engine.register(dlqName, meta.version, inline
|
|
1721
|
+
this.engine.register(dlqName, meta.version, inline, {
|
|
1722
|
+
origin
|
|
1723
|
+
});
|
|
1589
1724
|
deadLetterByWorkflow.set(meta.name, dlqName);
|
|
1590
1725
|
} else if (meta.deadLetterWorkflow) {
|
|
1591
1726
|
deadLetterByWorkflow.set(meta.name, workflowName2(meta.deadLetterWorkflow));
|
|
1592
1727
|
}
|
|
1593
1728
|
});
|
|
1729
|
+
this.reportUnattributed(unattributed);
|
|
1594
1730
|
this.installDeadLetterRouting(deadLetterByWorkflow);
|
|
1595
1731
|
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Says, once at boot, which workflows could not be attributed to a package — their runs are stamped
|
|
1734
|
+
* with no `origin` at all, so an operator filtering the dashboard by origin will not see them. That
|
|
1735
|
+
* is the deliberate trade (an absent origin over an invented one), but it has to be visible: a
|
|
1736
|
+
* silently-dropped run is indistinguishable from a run that never happened.
|
|
1737
|
+
*/
|
|
1738
|
+
reportUnattributed(names) {
|
|
1739
|
+
if (names.length === 0) return;
|
|
1740
|
+
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.`);
|
|
1741
|
+
}
|
|
1596
1742
|
/** Returns the instance's `@DeadLetter()` method bound to the instance, or undefined if none. */
|
|
1597
1743
|
findDeadLetterHandler(instance) {
|
|
1598
1744
|
const prototype = Object.getPrototypeOf(instance);
|