@dudousxd/nestjs-durable 0.39.1 → 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.cjs +133 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +133 -3
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
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
|
-
|
|
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);
|