@hostwebhook/node-sdk 0.1.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/code-runner.d.ts +20 -0
- package/dist/code-runner.js +138 -0
- package/dist/contratos.d.ts +121 -0
- package/dist/contratos.js +24 -0
- package/dist/dto/output-node.dto.d.ts +19 -0
- package/dist/dto/output-node.dto.js +96 -0
- package/dist/ensure-meta.d.ts +22 -0
- package/dist/ensure-meta.js +35 -0
- package/dist/execute-with-iteration.d.ts +18 -0
- package/dist/execute-with-iteration.js +66 -0
- package/dist/filter-utils.d.ts +22 -0
- package/dist/filter-utils.js +178 -0
- package/dist/handler-helpers.d.ts +21 -0
- package/dist/handler-helpers.js +53 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +73 -0
- package/dist/log-metadata.d.ts +191 -0
- package/dist/log-metadata.js +375 -0
- package/dist/node-dispatch.registry.d.ts +32 -0
- package/dist/node-dispatch.registry.js +45 -0
- package/dist/node-executors.d.ts +299 -0
- package/dist/node-executors.js +555 -0
- package/dist/node-lifecycle.d.ts +399 -0
- package/dist/node-lifecycle.js +782 -0
- package/dist/normalize-nodes.d.ts +18 -0
- package/dist/normalize-nodes.js +22 -0
- package/dist/output-node-ref.schema.d.ts +82 -0
- package/dist/output-node-ref.schema.js +90 -0
- package/dist/output-webhook-scope.d.ts +36 -0
- package/dist/output-webhook-scope.js +42 -0
- package/dist/payload-preview.d.ts +10 -0
- package/dist/payload-preview.js +39 -0
- package/dist/pipeline.constants.d.ts +29 -0
- package/dist/pipeline.constants.js +51 -0
- package/dist/pre-request-pool.d.ts +58 -0
- package/dist/pre-request-pool.js +308 -0
- package/dist/pre-request-runner-source.d.ts +28 -0
- package/dist/pre-request-runner-source.js +411 -0
- package/dist/regex-de-inquilino.d.ts +15 -0
- package/dist/regex-de-inquilino.js +98 -0
- package/dist/request-context.d.ts +18 -0
- package/dist/request-context.js +34 -0
- package/dist/retry-transient.d.ts +54 -0
- package/dist/retry-transient.js +67 -0
- package/dist/retry-utils.d.ts +17 -0
- package/dist/retry-utils.js +23 -0
- package/dist/schema-validator-utils.d.ts +9 -0
- package/dist/schema-validator-utils.js +140 -0
- package/dist/ssrf-guard.d.ts +202 -0
- package/dist/ssrf-guard.js +917 -0
- package/dist/swallow.d.ts +52 -0
- package/dist/swallow.js +55 -0
- package/dist/template-render.d.ts +33 -0
- package/dist/template-render.js +43 -0
- package/dist/try-parse.d.ts +41 -0
- package/dist/try-parse.js +69 -0
- package/dist/workspace-payloads.d.ts +66 -0
- package/dist/workspace-payloads.js +496 -0
- package/package.json +35 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catch handlers for work whose failure must not abort the caller.
|
|
3
|
+
*
|
|
4
|
+
* `.catch(() => {})` says "this may fail and that is fine". It is almost
|
|
5
|
+
* never true. What it actually says is "this may fail and nobody will ever
|
|
6
|
+
* know" — and this codebase has paid for that repeatedly: a TTL index that
|
|
7
|
+
* was silently discarded and expired nothing for four months, an S3 adapter
|
|
8
|
+
* built without a bucket whose every delete failed while uploads kept
|
|
9
|
+
* working, a merge that waited forever because no timeout job was enqueued.
|
|
10
|
+
* Each one was found by accident, long after it started costing something.
|
|
11
|
+
*
|
|
12
|
+
* There is a real category of work that genuinely should not abort a
|
|
13
|
+
* request: cache writes, `lastUsed` touches, best-effort cleanup. The
|
|
14
|
+
* problem was never that they are swallowed, it is that they are swallowed
|
|
15
|
+
* *anonymously*. These helpers keep the non-blocking behaviour and make the
|
|
16
|
+
* failure land in the log with a name attached.
|
|
17
|
+
*
|
|
18
|
+
* redis.set(k, v).catch(onFailure(this.logger, `cache write ${k}`));
|
|
19
|
+
* const limits = await plans.getOrgPlanLimits(orgId)
|
|
20
|
+
* .catch(onFailureReturn(this.logger, `plan limits for org ${orgId}`, null));
|
|
21
|
+
*
|
|
22
|
+
* NOT for use where the failure changes the answer. If a lookup failing
|
|
23
|
+
* means the caller returns 404 instead of 500, the fix is to let it throw,
|
|
24
|
+
* not to log it and carry on.
|
|
25
|
+
*/
|
|
26
|
+
import type { Logger } from '@nestjs/common';
|
|
27
|
+
/**
|
|
28
|
+
* Log and continue, yielding `undefined`.
|
|
29
|
+
*
|
|
30
|
+
* `what` should name the resource, not the operation — "cache write for
|
|
31
|
+
* workspace 69ab…" beats "redis failed", because the first one can be
|
|
32
|
+
* searched for and the second cannot.
|
|
33
|
+
*/
|
|
34
|
+
export declare function onFailure(logger: Logger, what: string): (err: unknown) => undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Log and continue with a fallback value.
|
|
37
|
+
*
|
|
38
|
+
* Use when the fallback is a genuine default rather than a disguise for the
|
|
39
|
+
* error — plan limits falling back to `null` is fine as long as the log says
|
|
40
|
+
* the tier could not be read, because otherwise a user silently gets the
|
|
41
|
+
* wrong ceiling and nothing anywhere says why.
|
|
42
|
+
*/
|
|
43
|
+
export declare function onFailureReturn<T>(logger: Logger, what: string, fallback: T): (err: unknown) => T;
|
|
44
|
+
/**
|
|
45
|
+
* Report the rejections inside an already-settled batch.
|
|
46
|
+
*
|
|
47
|
+
* `Promise.allSettled` never rejects, so `await Promise.allSettled(x).catch(…)`
|
|
48
|
+
* is dead code — three of those existed here, each looking like error
|
|
49
|
+
* handling while every individual rejection went unread. This awaits the
|
|
50
|
+
* batch and logs whichever entries failed.
|
|
51
|
+
*/
|
|
52
|
+
export declare function settleAll(promises: Promise<unknown>[], logger: Logger, what: string): Promise<void>;
|
package/dist/swallow.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.onFailure = onFailure;
|
|
4
|
+
exports.onFailureReturn = onFailureReturn;
|
|
5
|
+
exports.settleAll = settleAll;
|
|
6
|
+
function describe(err) {
|
|
7
|
+
if (err instanceof Error)
|
|
8
|
+
return err.stack ?? `${err.name}: ${err.message}`;
|
|
9
|
+
return String(err);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Log and continue, yielding `undefined`.
|
|
13
|
+
*
|
|
14
|
+
* `what` should name the resource, not the operation — "cache write for
|
|
15
|
+
* workspace 69ab…" beats "redis failed", because the first one can be
|
|
16
|
+
* searched for and the second cannot.
|
|
17
|
+
*/
|
|
18
|
+
function onFailure(logger, what) {
|
|
19
|
+
return (err) => {
|
|
20
|
+
logger.warn(`${what} failed (continuing): ${describe(err)}`);
|
|
21
|
+
return undefined;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Log and continue with a fallback value.
|
|
26
|
+
*
|
|
27
|
+
* Use when the fallback is a genuine default rather than a disguise for the
|
|
28
|
+
* error — plan limits falling back to `null` is fine as long as the log says
|
|
29
|
+
* the tier could not be read, because otherwise a user silently gets the
|
|
30
|
+
* wrong ceiling and nothing anywhere says why.
|
|
31
|
+
*/
|
|
32
|
+
function onFailureReturn(logger, what, fallback) {
|
|
33
|
+
return (err) => {
|
|
34
|
+
logger.warn(`${what} failed (using fallback): ${describe(err)}`);
|
|
35
|
+
return fallback;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Report the rejections inside an already-settled batch.
|
|
40
|
+
*
|
|
41
|
+
* `Promise.allSettled` never rejects, so `await Promise.allSettled(x).catch(…)`
|
|
42
|
+
* is dead code — three of those existed here, each looking like error
|
|
43
|
+
* handling while every individual rejection went unread. This awaits the
|
|
44
|
+
* batch and logs whichever entries failed.
|
|
45
|
+
*/
|
|
46
|
+
async function settleAll(promises, logger, what) {
|
|
47
|
+
if (promises.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
const results = await Promise.allSettled(promises);
|
|
50
|
+
const failed = results.filter((r) => r.status === 'rejected');
|
|
51
|
+
if (failed.length > 0) {
|
|
52
|
+
logger.warn(`${what}: ${failed.length}/${results.length} failed — ` +
|
|
53
|
+
failed.map((f) => describe(f.reason)).join(' | '));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized template render factory.
|
|
3
|
+
*
|
|
4
|
+
* Creates a render function bound to a specific execution context
|
|
5
|
+
* (payload, headers, meta, workspacePayloads). Ensures $() cross-node
|
|
6
|
+
* references always resolve when workspacePayloads is available.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const { render } = createRender({ payload, workspacePayloads, eventMeta });
|
|
10
|
+
* const url = render(entity.url);
|
|
11
|
+
* const body = render(entity.body);
|
|
12
|
+
*
|
|
13
|
+
* // Async version with _file resolution:
|
|
14
|
+
* const { renderAsync } = createRender({ payload, fileResolver });
|
|
15
|
+
* const body = await renderAsync(entity.body);
|
|
16
|
+
*/
|
|
17
|
+
import { type TemplateContext, type TemplateOptions, type FileResolver } from '@hostwebhook/template-engine';
|
|
18
|
+
export interface RenderContext {
|
|
19
|
+
payload: Record<string, unknown>;
|
|
20
|
+
headers?: Record<string, string>;
|
|
21
|
+
meta?: Record<string, string>;
|
|
22
|
+
eventMeta?: Record<string, string>;
|
|
23
|
+
workspacePayloads?: Record<string, Record<string, unknown>>;
|
|
24
|
+
/** Async file resolver — generates presigned download URLs for _file references */
|
|
25
|
+
fileResolver?: FileResolver;
|
|
26
|
+
}
|
|
27
|
+
export declare function createRender(rCtx: RenderContext, extraOpts?: Partial<TemplateOptions>): {
|
|
28
|
+
ctx: TemplateContext;
|
|
29
|
+
/** Sync render — does NOT resolve _file downloadUrl (use renderAsync for that) */
|
|
30
|
+
render: (t: string) => string;
|
|
31
|
+
/** Async render — pre-resolves _file references with presigned download URLs */
|
|
32
|
+
renderAsync: (t: string) => Promise<string>;
|
|
33
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Centralized template render factory.
|
|
4
|
+
*
|
|
5
|
+
* Creates a render function bound to a specific execution context
|
|
6
|
+
* (payload, headers, meta, workspacePayloads). Ensures $() cross-node
|
|
7
|
+
* references always resolve when workspacePayloads is available.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* const { render } = createRender({ payload, workspacePayloads, eventMeta });
|
|
11
|
+
* const url = render(entity.url);
|
|
12
|
+
* const body = render(entity.body);
|
|
13
|
+
*
|
|
14
|
+
* // Async version with _file resolution:
|
|
15
|
+
* const { renderAsync } = createRender({ payload, fileResolver });
|
|
16
|
+
* const body = await renderAsync(entity.body);
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.createRender = createRender;
|
|
20
|
+
const template_engine_1 = require("@hostwebhook/template-engine");
|
|
21
|
+
function createRender(rCtx, extraOpts) {
|
|
22
|
+
const ctx = {
|
|
23
|
+
payload: rCtx.payload,
|
|
24
|
+
headers: rCtx.headers ?? {},
|
|
25
|
+
meta: rCtx.eventMeta ?? rCtx.meta ?? {},
|
|
26
|
+
};
|
|
27
|
+
const opts = extraOpts || rCtx.workspacePayloads || rCtx.fileResolver
|
|
28
|
+
? {
|
|
29
|
+
...extraOpts,
|
|
30
|
+
...(rCtx.workspacePayloads
|
|
31
|
+
? { workspacePayloads: rCtx.workspacePayloads }
|
|
32
|
+
: {}),
|
|
33
|
+
...(rCtx.fileResolver ? { fileResolver: rCtx.fileResolver } : {}),
|
|
34
|
+
}
|
|
35
|
+
: undefined;
|
|
36
|
+
return {
|
|
37
|
+
ctx,
|
|
38
|
+
/** Sync render — does NOT resolve _file downloadUrl (use renderAsync for that) */
|
|
39
|
+
render: (t) => (0, template_engine_1.renderTemplate)(t, ctx, opts).output,
|
|
40
|
+
/** Async render — pre-resolves _file references with presigned download URLs */
|
|
41
|
+
renderAsync: async (t) => (await (0, template_engine_1.renderTemplateAsync)(t, ctx, opts)).output,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsers used as tests rather than as conversions.
|
|
3
|
+
*
|
|
4
|
+
* `try { JSON.parse(x) } catch {}` is not an error being swallowed — the throw
|
|
5
|
+
* IS the answer, and the caller wants "is this JSON?" rather than a failure.
|
|
6
|
+
* That makes it the one shape `common/swallow.ts` deliberately does not cover:
|
|
7
|
+
* logging it would emit a line per non-JSON payload, which is noise.
|
|
8
|
+
*
|
|
9
|
+
* But it was written out longhand 39 times across the codebase, each with its
|
|
10
|
+
* own comment explaining the same thing, and each leaving an empty `catch` for
|
|
11
|
+
* a reviewer to classify from scratch. The intent belongs in a name.
|
|
12
|
+
*
|
|
13
|
+
* const parsed = tryParseJson<Shape>(result.responseBody);
|
|
14
|
+
* if (parsed?._operationOutput) return parsed.output;
|
|
15
|
+
*
|
|
16
|
+
* The distinction that matters, and the reason this is a separate file from
|
|
17
|
+
* swallow.ts: use these ONLY where a parse failure is an expected, ordinary
|
|
18
|
+
* branch. If a payload that fails to parse means something is actually wrong —
|
|
19
|
+
* a webhook that should always be JSON, a config file, an API contract — let
|
|
20
|
+
* it throw, or catch it and say so. Reaching for `tryParseJson` to make an
|
|
21
|
+
* error quiet is the same mistake as `.catch(() => {})`, wearing a better name.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Parse JSON, or `null` when the input is not JSON.
|
|
25
|
+
*
|
|
26
|
+
* Returns `null` for `null`/`undefined`/empty input too, so callers do not
|
|
27
|
+
* need a separate emptiness check before asking.
|
|
28
|
+
*/
|
|
29
|
+
export declare function tryParseJson<T = unknown>(raw: unknown): T | null;
|
|
30
|
+
/**
|
|
31
|
+
* Parse JSON expecting an object, or `null`.
|
|
32
|
+
*
|
|
33
|
+
* `JSON.parse('4')` and `JSON.parse('"x"')` both succeed, so a bare
|
|
34
|
+
* `tryParseJson` followed by `parsed.someField` is a silent `undefined` on
|
|
35
|
+
* perfectly valid JSON that simply is not a record. Most call sites here want
|
|
36
|
+
* a record; this makes that explicit instead of relying on the field lookup
|
|
37
|
+
* to fail quietly.
|
|
38
|
+
*/
|
|
39
|
+
export declare function tryParseJsonObject<T extends object = Record<string, unknown>>(raw: unknown): T | null;
|
|
40
|
+
/** Parse a URL, or `null` when the string is not one. */
|
|
41
|
+
export declare function tryParseUrl(raw: unknown): URL | null;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parsers used as tests rather than as conversions.
|
|
4
|
+
*
|
|
5
|
+
* `try { JSON.parse(x) } catch {}` is not an error being swallowed — the throw
|
|
6
|
+
* IS the answer, and the caller wants "is this JSON?" rather than a failure.
|
|
7
|
+
* That makes it the one shape `common/swallow.ts` deliberately does not cover:
|
|
8
|
+
* logging it would emit a line per non-JSON payload, which is noise.
|
|
9
|
+
*
|
|
10
|
+
* But it was written out longhand 39 times across the codebase, each with its
|
|
11
|
+
* own comment explaining the same thing, and each leaving an empty `catch` for
|
|
12
|
+
* a reviewer to classify from scratch. The intent belongs in a name.
|
|
13
|
+
*
|
|
14
|
+
* const parsed = tryParseJson<Shape>(result.responseBody);
|
|
15
|
+
* if (parsed?._operationOutput) return parsed.output;
|
|
16
|
+
*
|
|
17
|
+
* The distinction that matters, and the reason this is a separate file from
|
|
18
|
+
* swallow.ts: use these ONLY where a parse failure is an expected, ordinary
|
|
19
|
+
* branch. If a payload that fails to parse means something is actually wrong —
|
|
20
|
+
* a webhook that should always be JSON, a config file, an API contract — let
|
|
21
|
+
* it throw, or catch it and say so. Reaching for `tryParseJson` to make an
|
|
22
|
+
* error quiet is the same mistake as `.catch(() => {})`, wearing a better name.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.tryParseJson = tryParseJson;
|
|
26
|
+
exports.tryParseJsonObject = tryParseJsonObject;
|
|
27
|
+
exports.tryParseUrl = tryParseUrl;
|
|
28
|
+
/**
|
|
29
|
+
* Parse JSON, or `null` when the input is not JSON.
|
|
30
|
+
*
|
|
31
|
+
* Returns `null` for `null`/`undefined`/empty input too, so callers do not
|
|
32
|
+
* need a separate emptiness check before asking.
|
|
33
|
+
*/
|
|
34
|
+
function tryParseJson(raw) {
|
|
35
|
+
if (typeof raw !== 'string' || raw.length === 0)
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse JSON expecting an object, or `null`.
|
|
46
|
+
*
|
|
47
|
+
* `JSON.parse('4')` and `JSON.parse('"x"')` both succeed, so a bare
|
|
48
|
+
* `tryParseJson` followed by `parsed.someField` is a silent `undefined` on
|
|
49
|
+
* perfectly valid JSON that simply is not a record. Most call sites here want
|
|
50
|
+
* a record; this makes that explicit instead of relying on the field lookup
|
|
51
|
+
* to fail quietly.
|
|
52
|
+
*/
|
|
53
|
+
function tryParseJsonObject(raw) {
|
|
54
|
+
const parsed = tryParseJson(raw);
|
|
55
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
56
|
+
return null;
|
|
57
|
+
return parsed;
|
|
58
|
+
}
|
|
59
|
+
/** Parse a URL, or `null` when the string is not one. */
|
|
60
|
+
function tryParseUrl(raw) {
|
|
61
|
+
if (typeof raw !== 'string' || raw.length === 0)
|
|
62
|
+
return null;
|
|
63
|
+
try {
|
|
64
|
+
return new URL(raw);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load lastPayload for referenced nodes in a workspace.
|
|
3
|
+
*
|
|
4
|
+
* Used by $("node name") in templates — provides synchronous
|
|
5
|
+
* lookup of any node's output within the same workspace.
|
|
6
|
+
*
|
|
7
|
+
* Optimized with Redis caching:
|
|
8
|
+
* - Extract $() refs from entity → only load referenced nodes
|
|
9
|
+
* - Redis MGET first (~1ms) → MongoDB fallback only for cache misses
|
|
10
|
+
* - cacheNodePayload warms the cache → subsequent lookups are instant
|
|
11
|
+
*/
|
|
12
|
+
import type { Db } from 'mongodb';
|
|
13
|
+
import type { Connection } from 'mongoose';
|
|
14
|
+
/** Get the shared Redis client (or null if not initialized) */
|
|
15
|
+
export declare function getPayloadRedis(): any;
|
|
16
|
+
/** Get or create the shared Redis client for payload caching */
|
|
17
|
+
export declare function initPayloadRedis(redisUrl?: string): void;
|
|
18
|
+
/** Extract node names referenced by $("name") or $('name') in the entity */
|
|
19
|
+
export declare function extractCrossNodeRefs(entity: unknown): Set<string>;
|
|
20
|
+
/** Write a node's payload to Redis cache (fire-and-forget, 5 min TTL) */
|
|
21
|
+
export declare function cacheNodePayload(wsId: string, name: string, payload: Record<string, unknown>): void;
|
|
22
|
+
/**
|
|
23
|
+
* Drop a deleted node's payload row and its Redis mirror.
|
|
24
|
+
*
|
|
25
|
+
* `saveLastPayload` creates the row and nothing ever removed it. Since the
|
|
26
|
+
* payload moved out of the entity and into `nodepayloads`, deleting a node
|
|
27
|
+
* left its output behind permanently: measured on production 2026-08-05,
|
|
28
|
+
* 25 of 192 rows (13%) belonged to nodes that no longer existed, spread
|
|
29
|
+
* across 13 node types, the oldest from 2026-07-31.
|
|
30
|
+
*
|
|
31
|
+
* The stale Redis key is the sharper edge. `$("node name")` resolves by
|
|
32
|
+
* NAME, not by id, so a new node reusing a deleted one's name would read the
|
|
33
|
+
* dead payload until the 5-minute TTL expired.
|
|
34
|
+
*
|
|
35
|
+
* Best-effort: the entity delete has already happened and a leftover row is
|
|
36
|
+
* inert, so failures are logged rather than thrown back at the caller.
|
|
37
|
+
*/
|
|
38
|
+
export declare function deleteNodePayload(connection: Connection, nodeId: string): Promise<void>;
|
|
39
|
+
/** Invalidate canvas-data Redis cache for an org+workspace (fire-and-forget) */
|
|
40
|
+
export declare function invalidateCanvasCache(orgId: string, workspaceId?: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Propagate a node rename to the `nodepayloads` collection and the
|
|
43
|
+
* Redis hot cache. Without this, cross-node `$("name")` lookups break
|
|
44
|
+
* silently after a rename: the template engine resolves $() by NAME
|
|
45
|
+
* (the data-picker emits names, not ObjectIds), but the persisted
|
|
46
|
+
* nodepayloads row keeps the old auto-generated name until the next
|
|
47
|
+
* dispatch overwrites it. The user-visible bug is "node fired, payload
|
|
48
|
+
* shows in the detail page, but downstream `$()` references render
|
|
49
|
+
* empty" — which produces hours of debugging chasing ghost workspace
|
|
50
|
+
* mismatches.
|
|
51
|
+
*
|
|
52
|
+
* Also wipes the old Redis cache key so a stale read doesn't shadow
|
|
53
|
+
* the new name, and re-warms under the new name when the row has a
|
|
54
|
+
* payload (so the very next $() lookup hits Redis, not Mongo).
|
|
55
|
+
*
|
|
56
|
+
* Fire-and-forget on individual failures — caller already returned
|
|
57
|
+
* the updated entity. Best-effort: the nodepayloads row is overwritten
|
|
58
|
+
* on the next dispatch anyway, so a one-off failure is recoverable.
|
|
59
|
+
*/
|
|
60
|
+
export declare function propagateNodeRename(connection: Connection, nodeId: string, previousName: string | undefined, newName: string, workspaceId: string | undefined): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Load workspace payloads for the given node names.
|
|
63
|
+
* Redis first (MGET, ~1ms), MongoDB fallback only for cache misses.
|
|
64
|
+
* If nodeNames is empty or undefined, returns {} instantly.
|
|
65
|
+
*/
|
|
66
|
+
export declare function loadWorkspacePayloads(db: Db, workspaceId: string, orgId: string, nodeNames?: Set<string>): Promise<Record<string, Record<string, unknown>>>;
|