@ductape/sdk 0.1.110 → 0.1.114
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/api/services/processorApi.service.d.ts +2 -0
- package/dist/api/services/processorApi.service.js +51 -0
- package/dist/api/services/processorApi.service.js.map +1 -1
- package/dist/api/services/productsApi.service.d.ts +7 -1
- package/dist/api/services/productsApi.service.js.map +1 -1
- package/dist/api/urls.d.ts +1 -0
- package/dist/api/urls.js +5 -4
- package/dist/api/urls.js.map +1 -1
- package/dist/context/execution-context.d.ts +37 -0
- package/dist/context/execution-context.js +38 -0
- package/dist/context/execution-context.js.map +1 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +18 -0
- package/dist/context/index.js.map +1 -0
- package/dist/database/adapters/mongodb.adapter.js +7 -7
- package/dist/database/adapters/mongodb.adapter.js.map +1 -1
- package/dist/database/databases.service.d.ts +4 -0
- package/dist/database/databases.service.js +7 -1
- package/dist/database/databases.service.js.map +1 -1
- package/dist/features/feature-executor.d.ts +19 -0
- package/dist/features/feature-executor.js +330 -98
- package/dist/features/feature-executor.js.map +1 -1
- package/dist/features/features.service.d.ts +21 -1
- package/dist/features/features.service.js +136 -33
- package/dist/features/features.service.js.map +1 -1
- package/dist/features/index.d.ts +2 -1
- package/dist/features/index.js +3 -1
- package/dist/features/index.js.map +1 -1
- package/dist/features/types/features.types.d.ts +17 -2
- package/dist/features/types/features.types.js.map +1 -1
- package/dist/functions/functions.runtime.d.ts +25 -0
- package/dist/functions/functions.runtime.js +278 -0
- package/dist/functions/functions.runtime.js.map +1 -0
- package/dist/functions/http-handler.d.ts +17 -0
- package/dist/functions/http-handler.js +51 -0
- package/dist/functions/http-handler.js.map +1 -0
- package/dist/functions/index.d.ts +3 -0
- package/dist/functions/index.js +20 -0
- package/dist/functions/index.js.map +1 -0
- package/dist/functions/types.d.ts +100 -0
- package/dist/functions/types.js +15 -0
- package/dist/functions/types.js.map +1 -0
- package/dist/graph/graphs.service.d.ts +1 -1
- package/dist/graph/graphs.service.js +73 -43
- package/dist/graph/graphs.service.js.map +1 -1
- package/dist/graph/types/traversal.interface.d.ts +2 -0
- package/dist/index.d.ts +18 -2
- package/dist/index.js +27 -3
- package/dist/index.js.map +1 -1
- package/dist/processor/services/processor.service.d.ts +6 -0
- package/dist/processor/services/processor.service.js +8 -13
- package/dist/processor/services/processor.service.js.map +1 -1
- package/dist/products/bootstrap-cache.js +7 -2
- package/dist/products/bootstrap-cache.js.map +1 -1
- package/dist/products/services/products.service.d.ts +2 -2
- package/dist/products/services/products.service.js +36 -1
- package/dist/products/services/products.service.js.map +1 -1
- package/dist/sessions/sessions.helper.js +13 -1
- package/dist/sessions/sessions.helper.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/processor.types.d.ts +15 -0
- package/dist/types/processor.types.js.map +1 -1
- package/dist/types/productsBuilder.types.d.ts +3 -0
- package/dist/types/productsBuilder.types.js +2 -0
- package/dist/types/productsBuilder.types.js.map +1 -1
- package/dist/vector/vector-database.service.d.ts +2 -0
- package/dist/vector/vector-database.service.js +21 -12
- package/dist/vector/vector-database.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -43,9 +43,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
43
43
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
44
44
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
45
45
|
};
|
|
46
|
+
var _a, _b;
|
|
46
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
-
exports.featureService = exports.FeatureService = exports.FeatureError = void 0;
|
|
48
|
+
exports.featureService = exports.FeatureCompilationError = exports.FeatureService = exports.FeatureError = void 0;
|
|
48
49
|
const crypto_1 = require("crypto");
|
|
50
|
+
const functions_1 = require("../functions");
|
|
49
51
|
const products_service_1 = __importDefault(require("../products/services/products.service"));
|
|
50
52
|
const processor_service_1 = __importDefault(require("../processor/services/processor.service"));
|
|
51
53
|
const featureApi_service_1 = require("../api/services/featureApi.service");
|
|
@@ -55,6 +57,15 @@ const processor_utils_1 = require("../processor/utils/processor.utils");
|
|
|
55
57
|
const cache_manager_1 = require("../cache/cache.manager");
|
|
56
58
|
const productsBuilder_types_1 = require("../types/productsBuilder.types");
|
|
57
59
|
const feature_executor_1 = require("./feature-executor");
|
|
60
|
+
// Feature compilation/execution used to emit dozens of synchronous console writes
|
|
61
|
+
// on every request. Preserve the diagnostic stream behind DUCTAPE_DEBUG without
|
|
62
|
+
// charging production feature latency for formatting and stdout I/O.
|
|
63
|
+
const featureDebugEnabled = typeof process !== 'undefined' && (((_a = process.env) === null || _a === void 0 ? void 0 : _a.DUCTAPE_DEBUG) === 'true' || ((_b = process.env) === null || _b === void 0 ? void 0 : _b.DUCTAPE_DEBUG) === '1');
|
|
64
|
+
const console = {
|
|
65
|
+
log: featureDebugEnabled ? globalThis.console.log.bind(globalThis.console) : (..._args) => { },
|
|
66
|
+
warn: globalThis.console.warn.bind(globalThis.console),
|
|
67
|
+
error: globalThis.console.error.bind(globalThis.console),
|
|
68
|
+
};
|
|
58
69
|
/**
|
|
59
70
|
* Error class for feature-related errors
|
|
60
71
|
*/
|
|
@@ -189,6 +200,7 @@ class FeatureService {
|
|
|
189
200
|
user_id: this.config.user_id,
|
|
190
201
|
token: this.config.token,
|
|
191
202
|
env_type: this.config.env_type,
|
|
203
|
+
redis_client: this.config.redis_client,
|
|
192
204
|
access_key: this.config.access_key,
|
|
193
205
|
workspace_private_key: this._privateKey || undefined,
|
|
194
206
|
});
|
|
@@ -302,7 +314,7 @@ class FeatureService {
|
|
|
302
314
|
// Persist to backend using ProductBuilder
|
|
303
315
|
const builder = await this.getProductBuilder(productTag);
|
|
304
316
|
console.log('[FeatureService] create calling builder.createFeature', { tag: featureData.tag });
|
|
305
|
-
await builder.createFeature(featureData);
|
|
317
|
+
const persisted = await builder.createFeature(featureData);
|
|
306
318
|
// Store locally
|
|
307
319
|
const localConfig = {
|
|
308
320
|
name: featureData.name,
|
|
@@ -318,6 +330,12 @@ class FeatureService {
|
|
|
318
330
|
};
|
|
319
331
|
this.localConfigs.set(featureData.tag, localConfig);
|
|
320
332
|
console.log('[FeatureService] create SUCCESS', { productTag, tag: featureData.tag });
|
|
333
|
+
return persisted;
|
|
334
|
+
}
|
|
335
|
+
/** True only when the remote catalogue read path can observe the feature. */
|
|
336
|
+
async exists(featureTag, productTag) {
|
|
337
|
+
const builder = await this.getProductBuilder(productTag);
|
|
338
|
+
return Boolean(await builder.fetchFeature(featureTag));
|
|
321
339
|
}
|
|
322
340
|
/**
|
|
323
341
|
* Fetch all features for a product
|
|
@@ -479,7 +497,16 @@ class FeatureService {
|
|
|
479
497
|
// If product is specified, create the feature
|
|
480
498
|
if (options.product) {
|
|
481
499
|
console.log('[FeatureService] define creating feature on backend', { product: options.product, tag: schema.tag });
|
|
482
|
-
await this.
|
|
500
|
+
const builder = await this.getProductBuilder(options.product);
|
|
501
|
+
const existing = await builder.fetchFeature(schema.tag);
|
|
502
|
+
if (existing)
|
|
503
|
+
await builder.updateFeature(schema.tag, schema);
|
|
504
|
+
else
|
|
505
|
+
await builder.createFeature(schema, true);
|
|
506
|
+
const persisted = await builder.fetchFeature(schema.tag);
|
|
507
|
+
if (!persisted) {
|
|
508
|
+
throw new FeatureError(`Feature ${schema.tag} was acknowledged but is absent from the catalogue read path`, 'FEATURE_CATALOGUE_INCONSISTENT');
|
|
509
|
+
}
|
|
483
510
|
}
|
|
484
511
|
// Store locally
|
|
485
512
|
const localConfig = {
|
|
@@ -505,6 +532,56 @@ class FeatureService {
|
|
|
505
532
|
schema,
|
|
506
533
|
};
|
|
507
534
|
}
|
|
535
|
+
/** Compile several definitions without performing any remote writes. */
|
|
536
|
+
async compileMany(definitions) {
|
|
537
|
+
const tags = definitions.map((definition) => definition.tag);
|
|
538
|
+
const duplicate = tags.find((tag, index) => tags.indexOf(tag) !== index);
|
|
539
|
+
if (duplicate)
|
|
540
|
+
throw FeatureError.validationError(`Duplicate feature tag in batch: ${duplicate}`);
|
|
541
|
+
return Promise.all(definitions.map((definition) => new FeatureCompiler(definition).compileAsync()));
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Publish a definition set with read-back verification. If a write fails, newly
|
|
545
|
+
* created records are removed and pre-existing records are restored.
|
|
546
|
+
*/
|
|
547
|
+
async publishMany(productTag, definitions, options = {}) {
|
|
548
|
+
const schemas = await this.compileMany(definitions);
|
|
549
|
+
if (options.dryRun)
|
|
550
|
+
return { product: productTag, dry_run: true, features: schemas };
|
|
551
|
+
const builder = await this.getProductBuilder(productTag);
|
|
552
|
+
const previous = new Map();
|
|
553
|
+
const changed = [];
|
|
554
|
+
try {
|
|
555
|
+
for (const schema of schemas) {
|
|
556
|
+
const existing = await builder.fetchFeature(schema.tag);
|
|
557
|
+
previous.set(schema.tag, existing);
|
|
558
|
+
if (existing)
|
|
559
|
+
await builder.updateFeature(schema.tag, schema);
|
|
560
|
+
else
|
|
561
|
+
await builder.createFeature(schema, true);
|
|
562
|
+
const readBack = await builder.fetchFeature(schema.tag);
|
|
563
|
+
if (!readBack)
|
|
564
|
+
throw new Error(`Feature ${schema.tag} is missing after publish`);
|
|
565
|
+
changed.push(schema.tag);
|
|
566
|
+
}
|
|
567
|
+
return { product: productTag, dry_run: false, features: schemas };
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
for (const tag of changed.reverse()) {
|
|
571
|
+
const old = previous.get(tag);
|
|
572
|
+
try {
|
|
573
|
+
if (old)
|
|
574
|
+
await builder.updateFeature(tag, old);
|
|
575
|
+
else
|
|
576
|
+
await builder.deleteFeature(tag);
|
|
577
|
+
}
|
|
578
|
+
catch (rollbackError) {
|
|
579
|
+
console.error('[Ductape] Feature batch rollback failed', { product: productTag, tag, rollbackError });
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
508
585
|
// ==================== WORKFLOW EXECUTION ====================
|
|
509
586
|
/**
|
|
510
587
|
* Execute a feature
|
|
@@ -1512,25 +1589,6 @@ function buildConditionFromScenario(scenario) {
|
|
|
1512
1589
|
}
|
|
1513
1590
|
return parts.length === 0 ? '' : parts.join(' && ');
|
|
1514
1591
|
}
|
|
1515
|
-
/** Create ctx.input that prefers recordInput values (for loops/switch) and falls back to $Input{field} for other keys. */
|
|
1516
|
-
function createHybridInputProxy(recordInput, inputProxy) {
|
|
1517
|
-
const base = recordInput !== null && recordInput !== void 0 ? recordInput : {};
|
|
1518
|
-
if (!inputProxy)
|
|
1519
|
-
return base;
|
|
1520
|
-
return new Proxy(base, {
|
|
1521
|
-
get(target, prop) {
|
|
1522
|
-
if (typeof prop !== 'string')
|
|
1523
|
-
return target[prop];
|
|
1524
|
-
if (prop in target && target[prop] !== undefined) {
|
|
1525
|
-
return target[prop];
|
|
1526
|
-
}
|
|
1527
|
-
return inputProxy[prop];
|
|
1528
|
-
},
|
|
1529
|
-
has(target, prop) {
|
|
1530
|
-
return prop in target || (inputProxy && prop in inputProxy);
|
|
1531
|
-
},
|
|
1532
|
-
});
|
|
1533
|
-
}
|
|
1534
1592
|
class RecordingContext {
|
|
1535
1593
|
constructor(_input, featureTag, featureInputSchema, stepResultOverrides, scenarioCondition, recordInput) {
|
|
1536
1594
|
this._steps = [];
|
|
@@ -1548,7 +1606,6 @@ class RecordingContext {
|
|
|
1548
1606
|
/** When set (from recordScenarios), every step we push gets this condition so it only runs when input matches. */
|
|
1549
1607
|
this._scenarioCondition = null;
|
|
1550
1608
|
this._recordInput = null;
|
|
1551
|
-
this._hybridInput = null;
|
|
1552
1609
|
this._featureTag = featureTag;
|
|
1553
1610
|
this._inputSchemaKeys = featureInputSchema ? Object.keys(featureInputSchema) : [];
|
|
1554
1611
|
this._stepResultOverrides = stepResultOverrides !== null && stepResultOverrides !== void 0 ? stepResultOverrides : {};
|
|
@@ -1557,12 +1614,17 @@ class RecordingContext {
|
|
|
1557
1614
|
if (this._inputSchemaKeys.length > 0) {
|
|
1558
1615
|
this._inputProxy = createInputProxy(this._inputSchemaKeys);
|
|
1559
1616
|
}
|
|
1560
|
-
this._hybridInput = createHybridInputProxy(this._recordInput, this._inputProxy);
|
|
1561
1617
|
}
|
|
1562
|
-
//
|
|
1618
|
+
// Runtime input is always represented by operators. Sample recording data must
|
|
1619
|
+
// never be allowed to become a production step literal.
|
|
1563
1620
|
get input() {
|
|
1564
|
-
var _a
|
|
1565
|
-
return ((
|
|
1621
|
+
var _a;
|
|
1622
|
+
return ((_a = this._inputProxy) !== null && _a !== void 0 ? _a : {});
|
|
1623
|
+
}
|
|
1624
|
+
// Explicit compile-time data for loops and branch discovery.
|
|
1625
|
+
get sampleInput() {
|
|
1626
|
+
var _a;
|
|
1627
|
+
return ((_a = this._recordInput) !== null && _a !== void 0 ? _a : {});
|
|
1566
1628
|
}
|
|
1567
1629
|
get feature_id() {
|
|
1568
1630
|
return `compile-${this._featureTag}`;
|
|
@@ -1617,10 +1679,15 @@ class RecordingContext {
|
|
|
1617
1679
|
try {
|
|
1618
1680
|
stepReturn = await handler();
|
|
1619
1681
|
}
|
|
1620
|
-
catch (
|
|
1621
|
-
|
|
1682
|
+
catch (error) {
|
|
1683
|
+
throw new FeatureCompilationError(this._featureTag, tag, `The step callback threw while compiling: ${error instanceof Error ? error.message : String(error)}. ` +
|
|
1684
|
+
'Feature callbacks may only use serializable ctx primitives or ctx.functions.');
|
|
1622
1685
|
}
|
|
1623
1686
|
this._pendingStep = null;
|
|
1687
|
+
if (!step.event) {
|
|
1688
|
+
throw new FeatureCompilationError(this._featureTag, tag, 'The step did not record a portable operation. Arbitrary application callbacks cannot be serialized. ' +
|
|
1689
|
+
'Use ctx.functions, ctx.action, ctx.database, ctx.events, ctx.storage, ctx.graph, ctx.vector, ctx.quota, or ctx.fallback.');
|
|
1690
|
+
}
|
|
1624
1691
|
// Parse and store step output in schema (operator strings for downstream $Step{tag}{field} references)
|
|
1625
1692
|
const valueToCapture = stepReturn !== undefined && stepReturn !== null && typeof stepReturn === 'object' && !Array.isArray(stepReturn)
|
|
1626
1693
|
? stepReturn.output !== undefined && stepReturn.process_id !== undefined
|
|
@@ -1899,6 +1966,27 @@ class RecordingContext {
|
|
|
1899
1966
|
},
|
|
1900
1967
|
};
|
|
1901
1968
|
}
|
|
1969
|
+
get functions() {
|
|
1970
|
+
const invoke = async (contract, operation, input) => {
|
|
1971
|
+
const reference = (0, functions_1.referenceFromContract)(contract, operation);
|
|
1972
|
+
this._updateCurrentStep(productsBuilder_types_1.FeatureStepType.FUNCTION, operation, {
|
|
1973
|
+
function_ref: reference,
|
|
1974
|
+
input,
|
|
1975
|
+
options: { timeout: reference.timeout_ms },
|
|
1976
|
+
});
|
|
1977
|
+
return createStepOutputProxy();
|
|
1978
|
+
};
|
|
1979
|
+
return {
|
|
1980
|
+
invoke,
|
|
1981
|
+
use: (contract) => new Proxy({}, {
|
|
1982
|
+
get: (_target, property) => {
|
|
1983
|
+
if (typeof property !== 'string' || !(property in contract.operations))
|
|
1984
|
+
return undefined;
|
|
1985
|
+
return (input) => invoke(contract, property, input);
|
|
1986
|
+
},
|
|
1987
|
+
}),
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1902
1990
|
get transform() {
|
|
1903
1991
|
return {
|
|
1904
1992
|
size: (obj) => Object.keys(obj).length,
|
|
@@ -2032,6 +2120,8 @@ class RecordingContext {
|
|
|
2032
2120
|
step.fallback = data.fallback;
|
|
2033
2121
|
if (data.feature)
|
|
2034
2122
|
step.feature = data.feature;
|
|
2123
|
+
if (data.function_ref)
|
|
2124
|
+
step.function_ref = data.function_ref;
|
|
2035
2125
|
if (data.input) {
|
|
2036
2126
|
step.input = convertToOperatorInput(data.input, this._inputProxy, this._inputSchemaKeys);
|
|
2037
2127
|
}
|
|
@@ -2150,7 +2240,7 @@ class FeatureCompiler {
|
|
|
2150
2240
|
* With recordInput, uses that as ctx.input so loops and control flow see real values.
|
|
2151
2241
|
*/
|
|
2152
2242
|
async compileAsync() {
|
|
2153
|
-
var _a, _b, _c, _d;
|
|
2243
|
+
var _a, _b, _c, _d, _e;
|
|
2154
2244
|
const scenarios = this.options.recordScenarios && this.options.recordScenarios.length > 0
|
|
2155
2245
|
? this.options.recordScenarios
|
|
2156
2246
|
: [(_a = this.options.recordInput) !== null && _a !== void 0 ? _a : {}];
|
|
@@ -2173,7 +2263,11 @@ class FeatureCompiler {
|
|
|
2173
2263
|
if (out !== undefined && out !== null)
|
|
2174
2264
|
handlerOutput = out;
|
|
2175
2265
|
}
|
|
2176
|
-
catch (
|
|
2266
|
+
catch (error) {
|
|
2267
|
+
if (error instanceof FeatureCompilationError)
|
|
2268
|
+
throw error;
|
|
2269
|
+
throw new FeatureCompilationError(this.options.tag, (_c = recordingCtx.current_step) !== null && _c !== void 0 ? _c : '(handler)', error instanceof Error ? error.message : String(error));
|
|
2270
|
+
}
|
|
2177
2271
|
for (const s of recordingCtx.getSteps()) {
|
|
2178
2272
|
const existing = stepsByTag.get(s.tag);
|
|
2179
2273
|
if (existing) {
|
|
@@ -2193,12 +2287,12 @@ class FeatureCompiler {
|
|
|
2193
2287
|
const finalSteps = stepOrder.map((tag) => stepsByTag.get(tag)).filter(Boolean);
|
|
2194
2288
|
const output = handlerOutput !== undefined && handlerOutput !== null && lastCtx
|
|
2195
2289
|
? lastCtx.captureOutput(handlerOutput)
|
|
2196
|
-
: ((
|
|
2290
|
+
: ((_d = this.options.output) !== null && _d !== void 0 ? _d : {});
|
|
2197
2291
|
return {
|
|
2198
2292
|
name: this.options.name,
|
|
2199
2293
|
tag: this.options.tag,
|
|
2200
2294
|
description: this.options.description,
|
|
2201
|
-
input: (
|
|
2295
|
+
input: (_e = this.options.input) !== null && _e !== void 0 ? _e : {},
|
|
2202
2296
|
output: output,
|
|
2203
2297
|
steps: finalSteps,
|
|
2204
2298
|
signals: this.options.signals
|
|
@@ -2218,6 +2312,15 @@ class FeatureCompiler {
|
|
|
2218
2312
|
};
|
|
2219
2313
|
}
|
|
2220
2314
|
}
|
|
2315
|
+
class FeatureCompilationError extends Error {
|
|
2316
|
+
constructor(featureTag, stepTag, message) {
|
|
2317
|
+
super(`Feature "${featureTag}", step "${stepTag}": ${message}`);
|
|
2318
|
+
this.featureTag = featureTag;
|
|
2319
|
+
this.stepTag = stepTag;
|
|
2320
|
+
this.name = 'FeatureCompilationError';
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
exports.FeatureCompilationError = FeatureCompilationError;
|
|
2221
2324
|
// Export singleton instance for convenience
|
|
2222
2325
|
exports.featureService = new FeatureService();
|
|
2223
2326
|
// Default export
|