@warlock.js/core 4.16.0 → 5.0.1
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/CHANGELOG.md +13 -0
- package/bin/warlock.js +154 -1
- package/esm/application/application.d.mts +65 -1
- package/esm/application/application.d.mts.map +1 -1
- package/esm/application/application.mjs +62 -0
- package/esm/application/application.mjs.map +1 -1
- package/esm/application/index.d.mts +1 -1
- package/esm/cli/cli-commands.manager.mjs +60 -10
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/commands/dev-server.command.mjs +1 -0
- package/esm/cli/commands/dev-server.command.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs +40 -0
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -0
- package/esm/cli/commands/doctor/checks/index.mjs +4 -2
- package/esm/cli/commands/doctor/checks/index.mjs.map +1 -1
- package/esm/commands/cli-command.d.mts +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -1
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/commands/types.d.mts +10 -2
- package/esm/commands/types.d.mts.map +1 -1
- package/esm/config/config-handlers.d.mts +1 -2
- package/esm/config/config-handlers.d.mts.map +1 -1
- package/esm/config/config-handlers.mjs +3 -2
- package/esm/config/config-handlers.mjs.map +1 -1
- package/esm/config/locale-configuration.mjs +29 -0
- package/esm/config/locale-configuration.mjs.map +1 -0
- package/esm/connectors/assert-no-reserved-connector-names.d.mts +7 -0
- package/esm/connectors/assert-no-reserved-connector-names.d.mts.map +1 -0
- package/esm/connectors/assert-no-reserved-connector-names.mjs +44 -0
- package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -0
- package/esm/connectors/assert-unique-connector-names.d.mts +7 -0
- package/esm/connectors/assert-unique-connector-names.d.mts.map +1 -0
- package/esm/connectors/assert-unique-connector-names.mjs +20 -0
- package/esm/connectors/assert-unique-connector-names.mjs.map +1 -0
- package/esm/connectors/connectors-manager.d.mts +38 -0
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +45 -12
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/describe-server-address.mjs +75 -0
- package/esm/connectors/describe-server-address.mjs.map +1 -0
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +6 -3
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/index.d.mts +4 -1
- package/esm/connectors/index.mjs +4 -1
- package/esm/connectors/register-configured-connectors.d.mts +14 -0
- package/esm/connectors/register-configured-connectors.d.mts.map +1 -0
- package/esm/connectors/register-configured-connectors.mjs +99 -0
- package/esm/connectors/register-configured-connectors.mjs.map +1 -0
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs +4 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/types.d.mts +76 -1
- package/esm/connectors/types.d.mts.map +1 -1
- package/esm/connectors/types.mjs.map +1 -1
- package/esm/database/models/database-log/database-log.d.mts +50 -2
- package/esm/database/models/database-log/database-log.d.mts.map +1 -1
- package/esm/database/utils.d.mts +2 -2
- package/esm/database/utils.d.mts.map +1 -1
- package/esm/dev-server/development-server.mjs +1 -0
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/layer-executor.mjs +2 -1
- package/esm/dev-server/layer-executor.mjs.map +1 -1
- package/esm/dev-server/read-config-ast.mjs +48 -0
- package/esm/dev-server/read-config-ast.mjs.map +1 -0
- package/esm/dev-server/run-typings-generation.mjs +18 -0
- package/esm/dev-server/run-typings-generation.mjs.map +1 -0
- package/esm/dev-server/type-generator.mjs +30 -39
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +169 -1
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/stubs.mjs +93 -1
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/context/request-context.d.mts +14 -3
- package/esm/http/context/request-context.d.mts.map +1 -1
- package/esm/http/context/request-context.mjs +11 -1
- package/esm/http/context/request-context.mjs.map +1 -1
- package/esm/http/context/request-memo.d.mts +21 -0
- package/esm/http/context/request-memo.d.mts.map +1 -0
- package/esm/http/context/request-memo.mjs +48 -0
- package/esm/http/context/request-memo.mjs.map +1 -0
- package/esm/http/database/RequestLog.d.mts +164 -2
- package/esm/http/database/RequestLog.d.mts.map +1 -1
- package/esm/http/index.d.mts +6 -5
- package/esm/http/index.mjs +2 -1
- package/esm/http/middleware/cache-response-middleware.d.mts +2 -2
- package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.mjs +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/index.d.mts +1 -1
- package/esm/http/middleware/index.mjs +1 -1
- package/esm/http/middleware/inject-request-context.d.mts +1 -8
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +9 -22
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/ip-filter.middleware.mjs +1 -1
- package/esm/http/middleware/ip-filter.middleware.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
- package/esm/http/middleware/rate-limit.middleware.mjs +1 -1
- package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.d.mts +0 -14
- package/esm/http/middleware/utils/idempotency-key.d.mts.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs +19 -1
- package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
- package/esm/http/request.d.mts +119 -30
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +98 -25
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.mjs +1 -1
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/types.d.mts +36 -1
- package/esm/http/types.d.mts.map +1 -1
- package/esm/http/uploaded-file.d.mts +51 -19
- package/esm/http/uploaded-file.d.mts.map +1 -1
- package/esm/http/uploaded-file.mjs +36 -8
- package/esm/http/uploaded-file.mjs.map +1 -1
- package/esm/index.d.mts +17 -9
- package/esm/index.mjs +10 -4
- package/esm/production/build-contributions.mjs +115 -0
- package/esm/production/build-contributions.mjs.map +1 -0
- package/esm/production/production-builder.mjs +97 -2
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/resolve-build-config.d.mts +25 -0
- package/esm/production/resolve-build-config.d.mts.map +1 -0
- package/esm/production/resolve-build-config.mjs +43 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs +20 -9
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/restful/restful.d.mts +29 -8
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs +31 -13
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/describe-route-for-log.mjs +29 -0
- package/esm/router/describe-route-for-log.mjs.map +1 -0
- package/esm/router/index.d.mts +3 -1
- package/esm/router/index.mjs +2 -0
- package/esm/router/log-request-lifecycle.mjs +38 -0
- package/esm/router/log-request-lifecycle.mjs.map +1 -0
- package/esm/router/positional-handler-diagnostics.d.mts +90 -0
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -0
- package/esm/router/positional-handler-diagnostics.mjs +247 -0
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -0
- package/esm/router/route-registry.d.mts +34 -0
- package/esm/router/route-registry.d.mts.map +1 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +87 -63
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +23 -5
- package/esm/router/types.d.mts.map +1 -1
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/storage.mjs +1 -1
- package/esm/storage/storage.mjs.map +1 -1
- package/esm/storage/utils/safe-fetch.d.mts +11 -1
- package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs +51 -4
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/tests/vitest-setup.mjs +1 -1
- package/esm/utils/load-environment.mjs +33 -2
- package/esm/utils/load-environment.mjs.map +1 -1
- package/esm/utils/types.d.mts +4 -0
- package/esm/utils/types.d.mts.map +1 -1
- package/esm/validation/validateAll.mjs +4 -1
- package/esm/validation/validateAll.mjs.map +1 -1
- package/esm/validation/validators/file-validator.d.mts +25 -1
- package/esm/validation/validators/file-validator.d.mts.map +1 -1
- package/esm/warlock-config/index.mjs +1 -1
- package/esm/warlock-config/normalize-build-config.mjs.map +1 -1
- package/esm/warlock-config/types.d.mts +36 -2
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/package.json +15 -12
- package/skills/request-memo/SKILL.md +142 -0
- package/skills/use-request-locals/SKILL.md +144 -0
|
@@ -27,30 +27,35 @@ var FilterApplicator = class {
|
|
|
27
27
|
if (typeof rule === "function") return {
|
|
28
28
|
type: "function",
|
|
29
29
|
fn: rule,
|
|
30
|
-
column: key
|
|
30
|
+
column: key,
|
|
31
|
+
key
|
|
31
32
|
};
|
|
32
33
|
if (Array.isArray(rule)) {
|
|
33
34
|
const [operator, target] = rule;
|
|
34
35
|
if (target === void 0) return {
|
|
35
36
|
type: operator,
|
|
36
37
|
column: key,
|
|
37
|
-
columns: void 0
|
|
38
|
+
columns: void 0,
|
|
39
|
+
key
|
|
38
40
|
};
|
|
39
41
|
if (Array.isArray(target)) return {
|
|
40
42
|
type: operator,
|
|
41
43
|
column: void 0,
|
|
42
|
-
columns: target
|
|
44
|
+
columns: target,
|
|
45
|
+
key
|
|
43
46
|
};
|
|
44
47
|
return {
|
|
45
48
|
type: operator,
|
|
46
49
|
column: target,
|
|
47
|
-
columns: void 0
|
|
50
|
+
columns: void 0,
|
|
51
|
+
key
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
54
|
return {
|
|
51
55
|
type: rule,
|
|
52
56
|
column: key,
|
|
53
|
-
columns: void 0
|
|
57
|
+
columns: void 0,
|
|
58
|
+
key
|
|
54
59
|
};
|
|
55
60
|
}
|
|
56
61
|
/**
|
|
@@ -66,7 +71,7 @@ var FilterApplicator = class {
|
|
|
66
71
|
handler.call(this, query, rule.column, rule.columns, value, options);
|
|
67
72
|
return;
|
|
68
73
|
}
|
|
69
|
-
this.applyWhereOperator(query, rule.type, rule.column, rule.columns, value);
|
|
74
|
+
this.applyWhereOperator(query, rule.type, rule.column, rule.columns, value, rule.key);
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
72
77
|
* Get filter handler for predefined types
|
|
@@ -114,7 +119,7 @@ var FilterApplicator = class {
|
|
|
114
119
|
/**
|
|
115
120
|
* Apply standard where operators
|
|
116
121
|
*/
|
|
117
|
-
applyWhereOperator(query, operator, column, columns, value) {
|
|
122
|
+
applyWhereOperator(query, operator, column, columns, value, ruleName) {
|
|
118
123
|
if (operator.startsWith("in") && operator !== "int" && !Array.isArray(value)) value = [value];
|
|
119
124
|
if (column) switch (operator) {
|
|
120
125
|
case "=":
|
|
@@ -149,11 +154,17 @@ var FilterApplicator = class {
|
|
|
149
154
|
query.whereNotBetween(column, value);
|
|
150
155
|
break;
|
|
151
156
|
}
|
|
152
|
-
else if (columns) {
|
|
157
|
+
else if (columns) if (operator === "=") {
|
|
153
158
|
const conditions = {};
|
|
154
159
|
for (const col of columns) conditions[col] = value;
|
|
155
160
|
query.orWhere(conditions);
|
|
156
|
-
}
|
|
161
|
+
} else if (operator === "like") query.orWhere((sub) => {
|
|
162
|
+
columns.forEach((col, index) => {
|
|
163
|
+
if (index === 0) sub.where(col, "like", value);
|
|
164
|
+
else sub.orWhere(col, "like", value);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
else throw new Error(`Unsupported multi-column filter operator "${operator}" for rule "${ruleName ?? columns.join(",")}". Multi-column filters only support "=" and "like".`);
|
|
157
168
|
}
|
|
158
169
|
/**
|
|
159
170
|
* Coerce a filter value to a boolean.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-applicator.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/filter-applicator.ts"],"sourcesContent":["import type {\n QueryBuilderContract as CascadeQueryBuilder,\n QueryBuilderContract,\n} from \"@warlock.js/cascade\";\nimport type { FilterOptions, FilterRule, FilterRules } from \"../../contracts\";\n\n/**\n * Applies repository filters to a Cascade-Next query builder\n * Translates repository filter rules into Cascade query builder method calls\n */\nexport class FilterApplicator {\n /**\n * Apply filters to a Cascade query builder\n *\n * @param query - Cascade query builder instance\n * @param filters - Filter structure defining how to filter\n * @param data - Data containing filter values\n * @param options - Additional filter options (date formats, etc.)\n */\n public apply(\n query: CascadeQueryBuilder<any>,\n filters: FilterRules,\n data: any,\n options: FilterOptions,\n ): void {\n for (const key in filters) {\n const value = data[key];\n if (value === undefined) continue;\n\n const rule = this.parseFilterRule(key, filters[key]);\n this.applyFilterRule(query, rule, value, data, options);\n }\n }\n\n /**\n * Parse a filter rule into a structured format\n */\n private parseFilterRule(key: string, rule: FilterRule) {\n // Handle custom function\n if (typeof rule === \"function\") {\n return { type: \"function\", fn: rule, column: key };\n }\n\n // Handle array format: [\"operator\"] or [\"operator\", \"column\"] or [\"operator\", [\"col1\", \"col2\"]]\n if (Array.isArray(rule)) {\n const [operator, target] = rule;\n\n if (target === undefined) {\n return { type: operator, column: key, columns: undefined };\n }\n\n if (Array.isArray(target)) {\n return { type: operator, column: undefined, columns: target };\n }\n\n return { type: operator, column: target, columns: undefined };\n }\n\n // Handle simple operator string\n return { type: rule, column: key, columns: undefined };\n }\n\n /**\n * Apply a single filter rule to the query\n */\n private applyFilterRule(\n query: CascadeQueryBuilder<any>,\n rule: any,\n value: any,\n data: any,\n options: FilterOptions,\n ): void {\n // 1. Custom function filter\n if (rule.type === \"function\") {\n rule.fn(value, query, data);\n return;\n }\n\n // 2. Predefined filter types\n const handler = this.getFilterHandler(rule.type);\n if (handler) {\n handler.call(this, query, rule.column, rule.columns, value, options);\n return;\n }\n\n // 3. Standard where operators\n this.applyWhereOperator(query, rule.type, rule.column, rule.columns, value);\n }\n\n /**\n * Get filter handler for predefined types\n */\n private getFilterHandler(type: string): Function | undefined {\n const handlers: Record<string, Function> = {\n // Boolean filters\n bool: this.handleBoolean,\n boolean: this.handleBoolean,\n\n // Numeric filters\n int: this.handleInt,\n integer: this.handleInt,\n \"!int\": this.handleNotInt,\n \"int>\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">\"),\n \"int>=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">=\"),\n \"int<\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<\"),\n \"int<=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<=\"),\n inInt: this.handleInInt,\n number: this.handleNumber,\n inNumber: this.handleInNumber,\n float: this.handleFloat,\n double: this.handleFloat,\n inFloat: this.handleInNumber,\n\n // Null filters\n null: this.handleNull,\n notNull: this.handleNotNull,\n \"!null\": this.handleNotNull,\n\n // Date filters\n date: this.handleDate,\n \"date>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">\"),\n \"date>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">=\"),\n \"date<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<\"),\n \"date<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<=\"),\n dateBetween: this.handleDateBetween,\n inDate: this.handleInDate,\n\n // DateTime filters\n dateTime: this.handleDateTime,\n \"dateTime>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">\"),\n \"dateTime>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">=\"),\n \"dateTime<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<\"),\n \"dateTime<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<=\"),\n dateTimeBetween: this.handleDateTimeBetween,\n inDateTime: this.handleInDateTime,\n\n // Scope filter - applies local scope when value is truthy\n scope: this.handleScope,\n\n // With filter - eager-loads a relation when value is truthy\n with: this.handleWith,\n\n // JoinWith filter - eager-loads a relation via SQL JOIN when value is truthy\n joinWith: this.handleJoinWith,\n\n // Vector similarity search — calls similarTo(column, embedding[])\n similarTo: this.handleSimilarTo,\n };\n\n return handlers[type];\n }\n\n /**\n * Apply standard where operators\n */\n private applyWhereOperator(\n query: QueryBuilderContract,\n operator: string,\n column?: string,\n columns?: string[],\n value?: any,\n ): void {\n // Handle \"in\" prefix for array values\n if (operator.startsWith(\"in\") && operator !== \"int\" && !Array.isArray(value)) {\n value = [value];\n }\n\n // Single column\n if (column) {\n switch (operator) {\n case \"=\":\n query.where(column, value);\n break;\n case \"!=\":\n case \"<>\":\n query.where(column, \"!=\", value);\n break;\n case \">\":\n case \">=\":\n case \"<\":\n case \"<=\":\n query.where(column, operator, value);\n break;\n case \"in\":\n query.whereIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"not in\":\n query.whereNotIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"like\":\n query.whereLike(column, value);\n break;\n case \"not like\":\n query.whereNotLike(column, value);\n break;\n case \"between\":\n query.whereBetween(column, value);\n break;\n case \"not between\":\n query.whereNotBetween(column, value);\n break;\n }\n }\n // Multiple columns (OR condition)\n else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = value;\n }\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // BOOLEAN FILTERS\n // ============================================================================\n\n /**\n * Coerce a filter value to a boolean.\n *\n * Recognized truthy forms: `true`, `1`, `\"true\"`, `\"1\"`.\n * Recognized falsy forms: `false`, `0`, `\"false\"`, `\"0\"`.\n * Anything else falls back to `Boolean(value)`.\n *\n * NOTE: this intentionally does NOT treat \"non-empty\" as true — the previous\n * `|| !isEmpty(value)` fallback coerced `false`/`0` to `true`, inverting the\n * filter for explicitly-false values.\n */\n private coerceBoolean(value: any): boolean {\n if (value === true || value === 1 || value === \"true\" || value === \"1\") {\n return true;\n }\n\n if (value === false || value === 0 || value === \"false\" || value === \"0\") {\n return false;\n }\n\n return Boolean(value);\n }\n\n private handleBoolean(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const boolValue = this.coerceBoolean(value);\n if (column) {\n query.where(column, boolValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = boolValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // NUMERIC FILTERS\n // ============================================================================\n private handleInt(query: QueryBuilderContract, column?: string, columns?: string[], value?: any) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, intValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = intValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleNotInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, \"!=\", intValue);\n } else if (columns) {\n // Use multiple orWhere calls for OR logic across columns\n for (const col of columns) {\n query.orWhere(col, \"!=\", intValue);\n }\n }\n }\n\n private handleIntComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n operator: string,\n ) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, operator, intValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere(col, operator, intValue);\n }\n }\n }\n\n private handleInInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => parseInt(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, values));\n }\n }\n }\n\n private handleNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const numValue = Number(value);\n if (column) {\n query.where(column, numValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = numValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleInNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => Number(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, values));\n }\n }\n }\n\n private handleFloat(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const floatValue = parseFloat(value);\n if (column) {\n query.where(column, floatValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = floatValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // NULL FILTERS\n // ============================================================================\n\n private handleNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNull(column);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere({ [col]: null });\n }\n }\n }\n\n private handleNotNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNotNull(column);\n } else if (columns) {\n // Use whereNotNull for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereNotNull(col));\n }\n }\n }\n\n // ============================================================================\n // SCOPE FILTER\n // ============================================================================\n\n /**\n * Handle scope filter - applies local scope and passes the filter value.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * active: \"scope\", // Uses the filter key as scope name\n * isAdmin: [\"scope\", \"admin\"] // Uses custom scope name\n * }\n * ```\n *\n * When list({ active: true }) is called, it will call query.scope(\"active\", true)\n * When list({ status: \"pending\" }) is called, it will call query.scope(\"status\", \"pending\")\n */\n private handleScope(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n // column holds the scope name (either the filter key or custom name from array format)\n if (column) {\n query.scope(column, value);\n }\n }\n\n // ============================================================================\n // WITH (EAGER LOAD) FILTER\n // ============================================================================\n\n /**\n * Handle with filter - eager-loads a relation when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"with\", \"ai_model\"], // load single relation\n * with_all: [\"with\", [\"ai_model\", \"unit\"]] // load multiple relations\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.with(\"ai_model\")\n */\n private handleWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n // Load a single named relation\n if (column) {\n if (query.with) {\n query.with(column);\n }\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n for (const relation of columns) {\n if (query.with) {\n query.with(relation);\n }\n }\n }\n }\n\n /**\n * Handle joinWith filter - eager-loads a relation via SQL JOIN when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"joinWith\", \"ai_model\"], // load single relation via join\n * with_all: [\"joinWith\", [\"ai_model\", \"unit\"]] // load multiple relations via join\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.joinWith(\"ai_model\")\n */\n private handleJoinWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n if (!query.joinWith) {\n console.warn(\n \"[Repository] joinWith is not supported by the query builder. using with instead.\",\n );\n return this.handleWith(query, column, columns, value);\n }\n\n // Load a single named relation\n if (column) {\n query.joinWith(column);\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n query.joinWith(...columns);\n }\n }\n\n // ============================================================================\n // VECTOR FILTER\n // ============================================================================\n\n /**\n * Handle similarTo filter — performs vector similarity search.\n *\n * The filter value must be a `number[]` (pre-computed embedding).\n * Delegates to `query.similarTo(column, embedding)` which is handled\n * driver-specifically (pgvector or MongoDB Atlas $vectorSearch).\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * organization_id: \"=\",\n * embedding: \"similarTo\",\n * }\n * ```\n *\n * Then in the service:\n * ```typescript\n * await vectorsRepository.list({ embedding: queryEmbedding, organization_id: orgId });\n * ```\n */\n private handleSimilarTo(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n if (!column || !Array.isArray(value)) return;\n\n // Cast to any: Cascade's internal QueryBuilderContract still uses nearestTo;\n // our wrapper (CascadeQueryBuilder.similarTo) delegates to it correctly.\n (query as any).similarTo(column, value);\n }\n\n // ============================================================================\n // DATE FILTERS\n // ============================================================================\n\n private handleDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n query.whereDate(column, dateValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereDate(col, dateValue));\n }\n }\n }\n\n private handleDateComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n if (operator === \">\" || operator === \">=\") {\n query.whereDateAfter(column, dateValue);\n } else {\n query.whereDateBefore(column, dateValue);\n }\n } else if (columns) {\n for (const col of columns) {\n if (operator === \">\" || operator === \">=\") {\n query.orWhere((q: any) => q.whereDateAfter(col, dateValue));\n } else {\n query.orWhere((q: any) => q.whereDateBefore(col, dateValue));\n }\n }\n }\n }\n\n private handleDateBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDate(v, options?.dateFormat));\n if (column) {\n query.whereDateBetween(column, [start, end]);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereDateBetween(col, [start, end]));\n }\n }\n }\n\n private handleInDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDate(v, options?.dateFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, dates));\n }\n }\n }\n\n // ============================================================================\n // DATETIME FILTERS\n // ============================================================================\n\n private handleDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, dateValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = dateValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleDateTimeComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, operator, dateValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere(col, operator, dateValue);\n }\n }\n }\n\n private handleDateTimeBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDateTime(v, options?.dateTimeFormat));\n if (column) {\n query.whereBetween(column, [start, end]);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereBetween(col, [start, end]));\n }\n }\n }\n\n private handleInDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDateTime(v, options?.dateTimeFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, dates));\n }\n }\n }\n\n // ============================================================================\n // DATE PARSING UTILITIES\n // ============================================================================\n\n /**\n * Parse date string to Date object\n * TODO: Implement proper date parsing with format support\n */\n private parseDate(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n return new Date(value);\n }\n\n /**\n * Parse datetime string to Date object\n * TODO: Implement proper datetime parsing with format support\n */\n private parseDateTime(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n return new Date(value);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filter-applicator.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/filter-applicator.ts"],"sourcesContent":["import type {\n QueryBuilderContract as CascadeQueryBuilder,\n QueryBuilderContract,\n} from \"@warlock.js/cascade\";\nimport type { FilterOptions, FilterRule, FilterRules } from \"../../contracts\";\n\n/**\n * Applies repository filters to a Cascade-Next query builder\n * Translates repository filter rules into Cascade query builder method calls\n */\nexport class FilterApplicator {\n /**\n * Apply filters to a Cascade query builder\n *\n * @param query - Cascade query builder instance\n * @param filters - Filter structure defining how to filter\n * @param data - Data containing filter values\n * @param options - Additional filter options (date formats, etc.)\n */\n public apply(\n query: CascadeQueryBuilder<any>,\n filters: FilterRules,\n data: any,\n options: FilterOptions,\n ): void {\n for (const key in filters) {\n const value = data[key];\n if (value === undefined) continue;\n\n const rule = this.parseFilterRule(key, filters[key]);\n this.applyFilterRule(query, rule, value, data, options);\n }\n }\n\n /**\n * Parse a filter rule into a structured format\n */\n private parseFilterRule(key: string, rule: FilterRule) {\n // Handle custom function\n if (typeof rule === \"function\") {\n return { type: \"function\", fn: rule, column: key, key };\n }\n\n // Handle array format: [\"operator\"] or [\"operator\", \"column\"] or [\"operator\", [\"col1\", \"col2\"]]\n if (Array.isArray(rule)) {\n const [operator, target] = rule;\n\n if (target === undefined) {\n return { type: operator, column: key, columns: undefined, key };\n }\n\n if (Array.isArray(target)) {\n return { type: operator, column: undefined, columns: target, key };\n }\n\n return { type: operator, column: target, columns: undefined, key };\n }\n\n // Handle simple operator string\n return { type: rule, column: key, columns: undefined, key };\n }\n\n /**\n * Apply a single filter rule to the query\n */\n private applyFilterRule(\n query: CascadeQueryBuilder<any>,\n rule: any,\n value: any,\n data: any,\n options: FilterOptions,\n ): void {\n // 1. Custom function filter\n if (rule.type === \"function\") {\n rule.fn(value, query, data);\n return;\n }\n\n // 2. Predefined filter types\n const handler = this.getFilterHandler(rule.type);\n if (handler) {\n handler.call(this, query, rule.column, rule.columns, value, options);\n return;\n }\n\n // 3. Standard where operators\n this.applyWhereOperator(query, rule.type, rule.column, rule.columns, value, rule.key);\n }\n\n /**\n * Get filter handler for predefined types\n */\n private getFilterHandler(type: string): Function | undefined {\n const handlers: Record<string, Function> = {\n // Boolean filters\n bool: this.handleBoolean,\n boolean: this.handleBoolean,\n\n // Numeric filters\n int: this.handleInt,\n integer: this.handleInt,\n \"!int\": this.handleNotInt,\n \"int>\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">\"),\n \"int>=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">=\"),\n \"int<\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<\"),\n \"int<=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<=\"),\n inInt: this.handleInInt,\n number: this.handleNumber,\n inNumber: this.handleInNumber,\n float: this.handleFloat,\n double: this.handleFloat,\n inFloat: this.handleInNumber,\n\n // Null filters\n null: this.handleNull,\n notNull: this.handleNotNull,\n \"!null\": this.handleNotNull,\n\n // Date filters\n date: this.handleDate,\n \"date>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">\"),\n \"date>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">=\"),\n \"date<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<\"),\n \"date<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<=\"),\n dateBetween: this.handleDateBetween,\n inDate: this.handleInDate,\n\n // DateTime filters\n dateTime: this.handleDateTime,\n \"dateTime>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">\"),\n \"dateTime>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">=\"),\n \"dateTime<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<\"),\n \"dateTime<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<=\"),\n dateTimeBetween: this.handleDateTimeBetween,\n inDateTime: this.handleInDateTime,\n\n // Scope filter - applies local scope when value is truthy\n scope: this.handleScope,\n\n // With filter - eager-loads a relation when value is truthy\n with: this.handleWith,\n\n // JoinWith filter - eager-loads a relation via SQL JOIN when value is truthy\n joinWith: this.handleJoinWith,\n\n // Vector similarity search — calls similarTo(column, embedding[])\n similarTo: this.handleSimilarTo,\n };\n\n return handlers[type];\n }\n\n /**\n * Apply standard where operators\n */\n private applyWhereOperator(\n query: QueryBuilderContract,\n operator: string,\n column?: string,\n columns?: string[],\n value?: any,\n ruleName?: string,\n ): void {\n // Handle \"in\" prefix for array values\n if (operator.startsWith(\"in\") && operator !== \"int\" && !Array.isArray(value)) {\n value = [value];\n }\n\n // Single column\n if (column) {\n switch (operator) {\n case \"=\":\n query.where(column, value);\n break;\n case \"!=\":\n case \"<>\":\n query.where(column, \"!=\", value);\n break;\n case \">\":\n case \">=\":\n case \"<\":\n case \"<=\":\n query.where(column, operator, value);\n break;\n case \"in\":\n query.whereIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"not in\":\n query.whereNotIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"like\":\n query.whereLike(column, value);\n break;\n case \"not like\":\n query.whereNotLike(column, value);\n break;\n case \"between\":\n query.whereBetween(column, value);\n break;\n case \"not between\":\n query.whereNotBetween(column, value);\n break;\n }\n }\n // Multiple columns (OR condition)\n else if (columns) {\n if (operator === \"=\") {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = value;\n }\n query.orWhere(conditions);\n } else if (operator === \"like\") {\n // (col0 LIKE value OR col1 LIKE value OR ...), OR'd with prior conditions —\n // the object form of orWhere() only ever emits \"=\", so \"like\" needs the\n // callback/group form to keep the operator instead of silently degrading\n // to equality (D1).\n query.orWhere((sub: QueryBuilderContract) => {\n columns.forEach((col, index) => {\n if (index === 0) {\n sub.where(col, \"like\", value);\n } else {\n sub.orWhere(col, \"like\", value);\n }\n });\n });\n } else {\n // Every other operator silently degraded to equality via the object form\n // of orWhere() — refuse instead of returning a correct-looking, wrong query.\n throw new Error(\n `Unsupported multi-column filter operator \"${operator}\" for rule \"${ruleName ?? columns.join(\",\")}\". ` +\n `Multi-column filters only support \"=\" and \"like\".`,\n );\n }\n }\n }\n\n // ============================================================================\n // BOOLEAN FILTERS\n // ============================================================================\n\n /**\n * Coerce a filter value to a boolean.\n *\n * Recognized truthy forms: `true`, `1`, `\"true\"`, `\"1\"`.\n * Recognized falsy forms: `false`, `0`, `\"false\"`, `\"0\"`.\n * Anything else falls back to `Boolean(value)`.\n *\n * NOTE: this intentionally does NOT treat \"non-empty\" as true — the previous\n * `|| !isEmpty(value)` fallback coerced `false`/`0` to `true`, inverting the\n * filter for explicitly-false values.\n */\n private coerceBoolean(value: any): boolean {\n if (value === true || value === 1 || value === \"true\" || value === \"1\") {\n return true;\n }\n\n if (value === false || value === 0 || value === \"false\" || value === \"0\") {\n return false;\n }\n\n return Boolean(value);\n }\n\n private handleBoolean(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const boolValue = this.coerceBoolean(value);\n if (column) {\n query.where(column, boolValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = boolValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // NUMERIC FILTERS\n // ============================================================================\n private handleInt(query: QueryBuilderContract, column?: string, columns?: string[], value?: any) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, intValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = intValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleNotInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, \"!=\", intValue);\n } else if (columns) {\n // Use multiple orWhere calls for OR logic across columns\n for (const col of columns) {\n query.orWhere(col, \"!=\", intValue);\n }\n }\n }\n\n private handleIntComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n operator: string,\n ) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, operator, intValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere(col, operator, intValue);\n }\n }\n }\n\n private handleInInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => parseInt(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, values));\n }\n }\n }\n\n private handleNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const numValue = Number(value);\n if (column) {\n query.where(column, numValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = numValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleInNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => Number(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, values));\n }\n }\n }\n\n private handleFloat(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const floatValue = parseFloat(value);\n if (column) {\n query.where(column, floatValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = floatValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // NULL FILTERS\n // ============================================================================\n\n private handleNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNull(column);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere({ [col]: null });\n }\n }\n }\n\n private handleNotNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNotNull(column);\n } else if (columns) {\n // Use whereNotNull for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereNotNull(col));\n }\n }\n }\n\n // ============================================================================\n // SCOPE FILTER\n // ============================================================================\n\n /**\n * Handle scope filter - applies local scope and passes the filter value.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * active: \"scope\", // Uses the filter key as scope name\n * isAdmin: [\"scope\", \"admin\"] // Uses custom scope name\n * }\n * ```\n *\n * When list({ active: true }) is called, it will call query.scope(\"active\", true)\n * When list({ status: \"pending\" }) is called, it will call query.scope(\"status\", \"pending\")\n */\n private handleScope(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n // column holds the scope name (either the filter key or custom name from array format)\n if (column) {\n query.scope(column, value);\n }\n }\n\n // ============================================================================\n // WITH (EAGER LOAD) FILTER\n // ============================================================================\n\n /**\n * Handle with filter - eager-loads a relation when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"with\", \"ai_model\"], // load single relation\n * with_all: [\"with\", [\"ai_model\", \"unit\"]] // load multiple relations\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.with(\"ai_model\")\n */\n private handleWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n // Load a single named relation\n if (column) {\n if (query.with) {\n query.with(column);\n }\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n for (const relation of columns) {\n if (query.with) {\n query.with(relation);\n }\n }\n }\n }\n\n /**\n * Handle joinWith filter - eager-loads a relation via SQL JOIN when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"joinWith\", \"ai_model\"], // load single relation via join\n * with_all: [\"joinWith\", [\"ai_model\", \"unit\"]] // load multiple relations via join\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.joinWith(\"ai_model\")\n */\n private handleJoinWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n if (!query.joinWith) {\n console.warn(\n \"[Repository] joinWith is not supported by the query builder. using with instead.\",\n );\n return this.handleWith(query, column, columns, value);\n }\n\n // Load a single named relation\n if (column) {\n query.joinWith(column);\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n query.joinWith(...columns);\n }\n }\n\n // ============================================================================\n // VECTOR FILTER\n // ============================================================================\n\n /**\n * Handle similarTo filter — performs vector similarity search.\n *\n * The filter value must be a `number[]` (pre-computed embedding).\n * Delegates to `query.similarTo(column, embedding)` which is handled\n * driver-specifically (pgvector or MongoDB Atlas $vectorSearch).\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * organization_id: \"=\",\n * embedding: \"similarTo\",\n * }\n * ```\n *\n * Then in the service:\n * ```typescript\n * await vectorsRepository.list({ embedding: queryEmbedding, organization_id: orgId });\n * ```\n */\n private handleSimilarTo(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n if (!column || !Array.isArray(value)) return;\n\n // Cast to any: Cascade's internal QueryBuilderContract still uses nearestTo;\n // our wrapper (CascadeQueryBuilder.similarTo) delegates to it correctly.\n (query as any).similarTo(column, value);\n }\n\n // ============================================================================\n // DATE FILTERS\n // ============================================================================\n\n private handleDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n query.whereDate(column, dateValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereDate(col, dateValue));\n }\n }\n }\n\n private handleDateComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n if (operator === \">\" || operator === \">=\") {\n query.whereDateAfter(column, dateValue);\n } else {\n query.whereDateBefore(column, dateValue);\n }\n } else if (columns) {\n for (const col of columns) {\n if (operator === \">\" || operator === \">=\") {\n query.orWhere((q: any) => q.whereDateAfter(col, dateValue));\n } else {\n query.orWhere((q: any) => q.whereDateBefore(col, dateValue));\n }\n }\n }\n }\n\n private handleDateBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDate(v, options?.dateFormat));\n if (column) {\n query.whereDateBetween(column, [start, end]);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereDateBetween(col, [start, end]));\n }\n }\n }\n\n private handleInDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDate(v, options?.dateFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, dates));\n }\n }\n }\n\n // ============================================================================\n // DATETIME FILTERS\n // ============================================================================\n\n private handleDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, dateValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = dateValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleDateTimeComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, operator, dateValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere(col, operator, dateValue);\n }\n }\n }\n\n private handleDateTimeBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDateTime(v, options?.dateTimeFormat));\n if (column) {\n query.whereBetween(column, [start, end]);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereBetween(col, [start, end]));\n }\n }\n }\n\n private handleInDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDateTime(v, options?.dateTimeFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, dates));\n }\n }\n }\n\n // ============================================================================\n // DATE PARSING UTILITIES\n // ============================================================================\n\n /**\n * Parse date string to Date object\n * TODO: Implement proper date parsing with format support\n */\n private parseDate(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n return new Date(value);\n }\n\n /**\n * Parse datetime string to Date object\n * TODO: Implement proper datetime parsing with format support\n */\n private parseDateTime(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n return new Date(value);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Request } from "../http/request.mjs";
|
|
2
2
|
import { Response } from "../http/response.mjs";
|
|
3
|
-
import { RestfulMiddleware, RouteResource } from "../router/types.mjs";
|
|
3
|
+
import { HttpContext, RestfulMiddleware, RouteResource } from "../router/types.mjs";
|
|
4
4
|
import { RepositoryManager } from "../repositories/repository.manager.mjs";
|
|
5
5
|
import { Model } from "@warlock.js/cascade";
|
|
6
6
|
|
|
@@ -39,31 +39,52 @@ declare abstract class Restful<T extends Model> implements RouteResource {
|
|
|
39
39
|
/**
|
|
40
40
|
* List records
|
|
41
41
|
*/
|
|
42
|
-
list(
|
|
42
|
+
list({
|
|
43
|
+
request,
|
|
44
|
+
response
|
|
45
|
+
}: HttpContext): Promise<Response | undefined>;
|
|
43
46
|
/**
|
|
44
47
|
* Get single record
|
|
45
48
|
*/
|
|
46
|
-
get(
|
|
49
|
+
get({
|
|
50
|
+
request,
|
|
51
|
+
response
|
|
52
|
+
}: HttpContext): Promise<Response | undefined>;
|
|
47
53
|
/**
|
|
48
54
|
* Create a new record
|
|
49
55
|
*/
|
|
50
|
-
create(
|
|
56
|
+
create({
|
|
57
|
+
request,
|
|
58
|
+
response
|
|
59
|
+
}: HttpContext): Promise<any>;
|
|
51
60
|
/**
|
|
52
61
|
* Update record
|
|
53
62
|
*/
|
|
54
|
-
update(
|
|
63
|
+
update({
|
|
64
|
+
request,
|
|
65
|
+
response
|
|
66
|
+
}: HttpContext): Promise<any>;
|
|
55
67
|
/**
|
|
56
68
|
* Patch record
|
|
57
69
|
*/
|
|
58
|
-
patch(
|
|
70
|
+
patch({
|
|
71
|
+
request,
|
|
72
|
+
response
|
|
73
|
+
}: HttpContext): Promise<Response | undefined>;
|
|
59
74
|
/**
|
|
60
75
|
* Delete record
|
|
61
76
|
*/
|
|
62
|
-
delete(
|
|
77
|
+
delete({
|
|
78
|
+
request,
|
|
79
|
+
response
|
|
80
|
+
}: HttpContext): Promise<Response | undefined>;
|
|
63
81
|
/**
|
|
64
82
|
* Bulk delete records
|
|
65
83
|
*/
|
|
66
|
-
bulkDelete(
|
|
84
|
+
bulkDelete({
|
|
85
|
+
request,
|
|
86
|
+
response
|
|
87
|
+
}: HttpContext): Promise<Response | undefined>;
|
|
67
88
|
/**
|
|
68
89
|
* Before create
|
|
69
90
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restful.d.mts","names":[],"sources":["../../../../../../../core/src/restful/restful.ts"],"mappings":";;;;;;;uBAOsB,OAAA,WAAkB,KAAA,aAAkB,aAAA;;;;YAI9C,UAAA,EAAY,iBAAA;;AAJxB;;YASY,UAAA;EAT4B;;;EAAA,UAc5B,eAAA;EAUU;;;EAAA,mBALD,UAAA,EAAY,iBAAA,CAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"restful.d.mts","names":[],"sources":["../../../../../../../core/src/restful/restful.ts"],"mappings":";;;;;;;uBAOsB,OAAA,WAAkB,KAAA,aAAkB,aAAA;;;;YAI9C,UAAA,EAAY,iBAAA;;AAJxB;;YASY,UAAA;EAT4B;;;EAAA,UAc5B,eAAA;EAUU;;;EAAA,mBALD,UAAA,EAAY,iBAAA,CAAkB,CAAA;EA8BpB;;;EAAA,UAzBnB,QAAA,EAAU,MAAA;EAyDD;;;;;EA7CZ,KAAA;EAkEwB;;;EA7DlB,IAAA,CAAK,EAAA,WAAU,OAAA,CAAA,CAAA;EA6GG;;;EArGlB,IAAA;IAAO,OAAA;IAAS;EAAA,GAAY,WAAA,GAAW,OAAA,CAAA,QAAA;EAiJtB;;;EAjHjB,GAAA;IAAM,OAAA;IAAS;EAAA,GAAY,WAAA,GAAW,OAAA,CAAA,QAAA;EAoJ7B;;;EA/HT,MAAA;IAAS,OAAA;IAAS;EAAA,GAAY,WAAA,GAAW,OAAA;EA+HA;;;EA/EzC,MAAA;IAAS,OAAA;IAAS;EAAA,GAAY,WAAA,GAAW,OAAA;EAgHI;;;EApE7C,KAAA;IAAQ,OAAA;IAAS;EAAA,GAAY,WAAA,GAAW,OAAA,CAAA,QAAA;EAoHyB;;;EAjFjE,MAAA;IAAS,OAAA;IAAS;EAAA,GAAY,WAAA,GAAW,OAAA,CAAA,QAAA;EAwFoB;;;EAvD7D,UAAA;IAAa,OAAA;IAAS;EAAA,GAAY,WAAA,GAAW,OAAA,CAAA,QAAA;EAiE/C;;;EAAA,UAjBK,YAAA,CAAa,QAAA,EAAU,OAAA,EAAS,SAAA,EAAW,QAAA,EAAU,OAAA,EAAS,CAAA,GAAI,OAAA;EA4BrE;;;EAAA,UArBG,QAAA,CAAS,QAAA,EAAU,OAAA,EAAS,SAAA,EAAW,QAAA,EAAU,OAAA,EAAS,CAAA,GAAI,OAAA;EA+BvC;;;EAAA,UAxBvB,YAAA,CACd,QAAA,EAAU,OAAA,EACV,SAAA,EAAW,QAAA,EACX,OAAA,EAAS,CAAA,EACT,UAAA,GAAa,CAAA,GACZ,OAAA;EA0BgC;;;EAAA,UAnBnB,QAAA,CACd,QAAA,EAAU,OAAA,EACV,SAAA,EAAW,QAAA,EACX,OAAA,EAAS,CAAA,EACT,UAAA,EAAY,CAAA,GACX,OAAA;EAsBS;;;EAAA,UAfI,YAAA,CAAa,QAAA,EAAU,OAAA,EAAS,SAAA,EAAW,QAAA,EAAU,OAAA,EAAS,CAAA,GAAI,OAAA;EAmB/E;;;EAAA,UAZa,QAAA,CAAS,QAAA,EAAU,OAAA,EAAS,SAAA,EAAW,QAAA,EAAU,OAAA,EAAS,CAAA,GAAI,OAAA;EAuBhE;;;EAAA,UAhBE,WAAA,CACd,QAAA,EAAU,OAAA,EACV,SAAA,EAAW,QAAA,EACX,OAAA,EAAS,CAAA,EACT,UAAA,GAAa,CAAA,GACZ,OAAA;EAsBS;;;EAAA,UAfI,OAAA,CACd,QAAA,EAAU,OAAA,EACV,SAAA,EAAW,QAAA,EACX,OAAA,EAAS,CAAA,EACT,UAAA,EAAY,CAAA,GACX,OAAA;EAqBU;;;EAAA,UAdG,UAAA,CACd,QAAA,EAAU,OAAA,EACV,SAAA,EAAW,QAAA,EACX,OAAA,GAAU,CAAA,EACV,UAAA,GAAa,CAAA,GACZ,OAAA;EAsBQ;;;EAAA,UAfK,MAAA,CACd,QAAA,EAAU,OAAA,EACV,SAAA,EAAW,QAAA,EACX,OAAA,EAAS,CAAA,EACT,UAAA,GAAa,CAAA,GACZ,OAAA;EAYY;;;;EAAA,UAJC,cAAA,CACd,MAAA,UACA,OAAA,EAAS,OAAA,EACT,QAAA,EAAU,QAAA,EACV,OAAA,SAAa,OAAA,SAAA,MAAA,gBAAA,QAAA;AAAA"}
|
package/esm/restful/restful.mjs
CHANGED
|
@@ -24,7 +24,7 @@ var Restful = class {
|
|
|
24
24
|
/**
|
|
25
25
|
* List records
|
|
26
26
|
*/
|
|
27
|
-
async list(request, response) {
|
|
27
|
+
async list({ request, response }) {
|
|
28
28
|
try {
|
|
29
29
|
if (await this.callMiddleware("list", request, response)) return;
|
|
30
30
|
const responseDocument = {};
|
|
@@ -43,7 +43,7 @@ var Restful = class {
|
|
|
43
43
|
/**
|
|
44
44
|
* Get single record
|
|
45
45
|
*/
|
|
46
|
-
async get(request, response) {
|
|
46
|
+
async get({ request, response }) {
|
|
47
47
|
try {
|
|
48
48
|
if (await this.callMiddleware("get", request, response)) return;
|
|
49
49
|
const record = await this.find(request.input("id"));
|
|
@@ -56,7 +56,7 @@ var Restful = class {
|
|
|
56
56
|
/**
|
|
57
57
|
* Create a new record
|
|
58
58
|
*/
|
|
59
|
-
async create(request, response) {
|
|
59
|
+
async create({ request, response }) {
|
|
60
60
|
try {
|
|
61
61
|
const model = this.repository.newModel();
|
|
62
62
|
const beforeCreate = await this.beforeCreate(request, response, model);
|
|
@@ -68,7 +68,10 @@ var Restful = class {
|
|
|
68
68
|
if (createOutput) return createOutput;
|
|
69
69
|
const saveOutput = await this.onSave(request, response, record);
|
|
70
70
|
if (saveOutput) return saveOutput;
|
|
71
|
-
if (this.returnOn.create === "records") return this.list(
|
|
71
|
+
if (this.returnOn.create === "records") return this.list({
|
|
72
|
+
request,
|
|
73
|
+
response
|
|
74
|
+
});
|
|
72
75
|
return response.successCreate({ [this.recordName]: record });
|
|
73
76
|
} catch (error) {
|
|
74
77
|
log.error("restful", "create", error);
|
|
@@ -78,7 +81,7 @@ var Restful = class {
|
|
|
78
81
|
/**
|
|
79
82
|
* Update record
|
|
80
83
|
*/
|
|
81
|
-
async update(request, response) {
|
|
84
|
+
async update({ request, response }) {
|
|
82
85
|
try {
|
|
83
86
|
const record = await this.find(request.input("id"));
|
|
84
87
|
if (!record) return response.notFound({ error: "Record not found" });
|
|
@@ -90,7 +93,10 @@ var Restful = class {
|
|
|
90
93
|
await record.save(request.allExceptParams());
|
|
91
94
|
this.onUpdate(request, response, record, oldRecord);
|
|
92
95
|
this.onSave(request, response, record, oldRecord);
|
|
93
|
-
if (this.returnOn.update === "records") return this.list(
|
|
96
|
+
if (this.returnOn.update === "records") return this.list({
|
|
97
|
+
request,
|
|
98
|
+
response
|
|
99
|
+
});
|
|
94
100
|
return response.success({ [this.recordName]: record });
|
|
95
101
|
} catch (error) {
|
|
96
102
|
log.error("restful", "update", error);
|
|
@@ -99,7 +105,7 @@ var Restful = class {
|
|
|
99
105
|
/**
|
|
100
106
|
* Patch record
|
|
101
107
|
*/
|
|
102
|
-
async patch(request, response) {
|
|
108
|
+
async patch({ request, response }) {
|
|
103
109
|
try {
|
|
104
110
|
const record = await this.find(request.input("id"));
|
|
105
111
|
if (!record) return response.notFound({ error: "Record not found" });
|
|
@@ -109,7 +115,10 @@ var Restful = class {
|
|
|
109
115
|
await record.save(request.heavyExceptParams());
|
|
110
116
|
this.onPatch(request, response, record, oldRecord);
|
|
111
117
|
this.onSave(request, response, record, oldRecord);
|
|
112
|
-
if (this.returnOn.patch === "records") return this.list(
|
|
118
|
+
if (this.returnOn.patch === "records") return this.list({
|
|
119
|
+
request,
|
|
120
|
+
response
|
|
121
|
+
});
|
|
113
122
|
return response.success({ [this.recordName]: record });
|
|
114
123
|
} catch (error) {
|
|
115
124
|
log.error("restful", "patch", error);
|
|
@@ -118,7 +127,7 @@ var Restful = class {
|
|
|
118
127
|
/**
|
|
119
128
|
* Delete record
|
|
120
129
|
*/
|
|
121
|
-
async delete(request, response) {
|
|
130
|
+
async delete({ request, response }) {
|
|
122
131
|
try {
|
|
123
132
|
const record = await this.find(request.input("id"));
|
|
124
133
|
if (!record) return response.notFound();
|
|
@@ -126,7 +135,10 @@ var Restful = class {
|
|
|
126
135
|
await this.beforeDelete(request, response, record);
|
|
127
136
|
await record.destroy();
|
|
128
137
|
this.onDelete(request, response, record);
|
|
129
|
-
if (this.returnOn.delete === "records") return this.list(
|
|
138
|
+
if (this.returnOn.delete === "records") return this.list({
|
|
139
|
+
request,
|
|
140
|
+
response
|
|
141
|
+
});
|
|
130
142
|
return response.success();
|
|
131
143
|
} catch (error) {
|
|
132
144
|
log.error("restful", "delete", error);
|
|
@@ -136,7 +148,7 @@ var Restful = class {
|
|
|
136
148
|
/**
|
|
137
149
|
* Bulk delete records
|
|
138
150
|
*/
|
|
139
|
-
async bulkDelete(request, response) {
|
|
151
|
+
async bulkDelete({ request, response }) {
|
|
140
152
|
try {
|
|
141
153
|
const ids = request.input("id");
|
|
142
154
|
if (!Array.isArray(ids)) return response.badRequest({ error: "id must be an array" });
|
|
@@ -147,7 +159,10 @@ var Restful = class {
|
|
|
147
159
|
await record.destroy();
|
|
148
160
|
this.onDelete(request, response, record);
|
|
149
161
|
}));
|
|
150
|
-
if (this.returnOn.delete === "records") return this.list(
|
|
162
|
+
if (this.returnOn.delete === "records") return this.list({
|
|
163
|
+
request,
|
|
164
|
+
response
|
|
165
|
+
});
|
|
151
166
|
return response.success({ deleted: records.length });
|
|
152
167
|
} catch (error) {
|
|
153
168
|
log.error("restful", "bulkDelete", error);
|
|
@@ -201,7 +216,10 @@ var Restful = class {
|
|
|
201
216
|
async callMiddleware(method, request, response, _record) {
|
|
202
217
|
if (!this.middleware[method]) return;
|
|
203
218
|
for (const middleware of this.middleware[method]) {
|
|
204
|
-
const output = await middleware(
|
|
219
|
+
const output = await middleware({
|
|
220
|
+
request,
|
|
221
|
+
response
|
|
222
|
+
});
|
|
205
223
|
if (output) return output;
|
|
206
224
|
}
|
|
207
225
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restful.mjs","names":[],"sources":["../../../../../../../core/src/restful/restful.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\nimport type { Model } from \"@warlock.js/cascade\";\nimport { log } from \"@warlock.js/logger\";\nimport type { Request, Response } from \"../http\";\nimport type { QueryBuilderContract, RepositoryManager } from \"../repositories\";\nimport type { RestfulMiddleware, RouteResource } from \"../router\";\n\nexport abstract class Restful<T extends Model> implements RouteResource {\n /**\n * Middleware for each method\n */\n protected middleware: RestfulMiddleware = {};\n\n /**\n * Record name\n */\n protected recordName = \"record\";\n\n /**\n * Records list name\n */\n protected recordsListName = \"records\";\n\n /**\n * Repository\n */\n protected abstract repository: RepositoryManager<T>;\n\n /**\n * Define what to be returned when a record is created|updated|deleted|patched\n */\n protected returnOn: Record<string, \"record\" | \"records\"> = {\n create: \"record\",\n update: \"record\",\n delete: \"record\",\n patch: \"record\",\n };\n\n /**\n * Enable fetching cache\n *\n * @default true\n */\n public cache = true;\n\n /**\n * Find record instance by id\n */\n public async find(id: number) {\n const findMethod = this.cache ? \"getCached\" : \"find\";\n return this.repository[findMethod](id);\n }\n\n /**\n * List records\n */\n public async list(request: Request, response: Response) {\n try {\n if (await this.callMiddleware(\"list\", request, response)) return;\n\n const responseDocument: GenericObject = {};\n\n const data = request.heavy();\n\n if (data.paginate === \"false\") {\n data.paginate = false;\n }\n\n const listMethod = this.cache ? \"listCached\" : \"list\";\n\n const { data: documents, pagination } = await this.repository[listMethod](data);\n\n responseDocument[this.recordsListName] = documents;\n\n if (pagination) {\n responseDocument.pagination = pagination;\n }\n\n return response.success(responseDocument);\n } catch (error: any) {\n log.error(\"restful\", \"list\", error);\n return response.serverError(error);\n }\n }\n\n /**\n * Get single record\n */\n public async get(request: Request, response: Response) {\n try {\n if (await this.callMiddleware(\"get\", request, response)) return;\n\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound();\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"get\", error);\n }\n }\n\n /**\n * Create a new record\n */\n public async create(request: Request, response: Response) {\n try {\n const model = this.repository.newModel();\n const beforeCreate = await this.beforeCreate(request, response, model);\n\n if (beforeCreate) {\n return beforeCreate;\n }\n\n const beforeSave = await this.beforeSave(request, response, model);\n\n if (beforeSave) {\n return beforeSave;\n }\n\n const record = await this.repository.create(request.all());\n\n const createOutput = await this.onCreate(request, response, record);\n\n if (createOutput) {\n return createOutput;\n }\n\n const saveOutput = await this.onSave(request, response, record);\n\n if (saveOutput) {\n return saveOutput;\n }\n\n if (this.returnOn.create === \"records\") {\n return this.list(request, response);\n }\n\n return response.successCreate({\n [this.recordName]: record,\n });\n } catch (error: Error | any) {\n log.error(\"restful\", \"create\", error);\n\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Update record\n */\n public async update(request: Request, response: Response) {\n try {\n // Find record\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound({\n error: \"Record not found\",\n });\n }\n\n const beforeOutput = await this.beforeUpdate(request, response, record);\n if (beforeOutput) {\n return beforeOutput;\n }\n\n const beforeSafe = await this.beforeSave(request, response, record);\n\n if (beforeSafe) {\n return beforeSafe;\n }\n\n const oldRecord = record.clone();\n\n await record.save(request.allExceptParams());\n\n this.onUpdate(request, response, record, oldRecord);\n this.onSave(request, response, record, oldRecord);\n\n if (this.returnOn.update === \"records\") {\n return this.list(request, response);\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"update\", error);\n }\n }\n\n /**\n * Patch record\n */\n public async patch(request: Request, response: Response) {\n try {\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound({\n error: \"Record not found\",\n });\n }\n\n const oldRecord = record.clone();\n\n await this.beforePatch(request, response, record, oldRecord);\n await this.beforeSave(request, response, record, oldRecord);\n\n await record.save(request.heavyExceptParams());\n\n this.onPatch(request, response, record, oldRecord);\n this.onSave(request, response, record, oldRecord);\n\n if (this.returnOn.patch === \"records\") {\n return this.list(request, response);\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"patch\", error);\n }\n }\n\n /**\n * Delete record\n */\n public async delete(request: Request, response: Response) {\n try {\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound();\n }\n\n if (await this.callMiddleware(\"delete\", request, response, record)) return;\n\n await this.beforeDelete(request, response, record);\n\n await record.destroy();\n\n this.onDelete(request, response, record);\n\n if (this.returnOn.delete === \"records\") {\n return this.list(request, response);\n }\n\n return response.success();\n } catch (error: Error | any) {\n log.error(\"restful\", \"delete\", error);\n\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Bulk delete records\n */\n public async bulkDelete(request: Request, response: Response) {\n try {\n const ids = request.input(\"id\");\n\n if (!Array.isArray(ids)) {\n return response.badRequest({\n error: \"id must be an array\",\n });\n }\n\n const records = await this.repository.all({\n perform: (query: QueryBuilderContract<T>) =>\n query.whereIn(\n \"id\",\n ids.map((id) => parseInt(id)),\n ),\n });\n\n await Promise.all(\n records.map(async (record) => {\n if (await this.callMiddleware(\"delete\", request, response, record)) {\n return;\n }\n\n await this.beforeDelete(request, response, record);\n await record.destroy();\n this.onDelete(request, response, record);\n }),\n );\n\n if (this.returnOn.delete === \"records\") {\n return this.list(request, response);\n }\n\n return response.success({\n deleted: records.length,\n });\n } catch (error: Error | any) {\n log.error(\"restful\", \"bulkDelete\", error);\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Before create\n */\n protected async beforeCreate(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * On create\n */\n protected async onCreate(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * Before update\n */\n protected async beforeUpdate(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On update\n */\n protected async onUpdate(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Before delete\n */\n protected async beforeDelete(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * On delete\n */\n protected async onDelete(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * Before patch\n */\n protected async beforePatch(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On patch\n */\n protected async onPatch(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Before save\n */\n protected async beforeSave(\n _request: Request,\n _response: Response,\n _record?: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On save\n */\n protected async onSave(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Call middleware for the given method\n *\n */\n protected async callMiddleware(\n method: string,\n request: Request,\n response: Response,\n _record?: any,\n ) {\n if (!this.middleware[method]) return;\n\n for (const middleware of this.middleware[method]) {\n const output = await middleware(request, response);\n\n if (output) {\n return output;\n }\n }\n\n return;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"restful.mjs","names":[],"sources":["../../../../../../../core/src/restful/restful.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\nimport type { Model } from \"@warlock.js/cascade\";\nimport { log } from \"@warlock.js/logger\";\nimport type { Request, Response } from \"../http\";\nimport type { QueryBuilderContract, RepositoryManager } from \"../repositories\";\nimport type { HttpContext, RestfulMiddleware, RouteResource } from \"../router\";\n\nexport abstract class Restful<T extends Model> implements RouteResource {\n /**\n * Middleware for each method\n */\n protected middleware: RestfulMiddleware = {};\n\n /**\n * Record name\n */\n protected recordName = \"record\";\n\n /**\n * Records list name\n */\n protected recordsListName = \"records\";\n\n /**\n * Repository\n */\n protected abstract repository: RepositoryManager<T>;\n\n /**\n * Define what to be returned when a record is created|updated|deleted|patched\n */\n protected returnOn: Record<string, \"record\" | \"records\"> = {\n create: \"record\",\n update: \"record\",\n delete: \"record\",\n patch: \"record\",\n };\n\n /**\n * Enable fetching cache\n *\n * @default true\n */\n public cache = true;\n\n /**\n * Find record instance by id\n */\n public async find(id: number) {\n const findMethod = this.cache ? \"getCached\" : \"find\";\n return this.repository[findMethod](id);\n }\n\n /**\n * List records\n */\n public async list({ request, response }: HttpContext) {\n try {\n if (await this.callMiddleware(\"list\", request, response)) return;\n\n const responseDocument: GenericObject = {};\n\n const data = request.heavy();\n\n if (data.paginate === \"false\") {\n data.paginate = false;\n }\n\n const listMethod = this.cache ? \"listCached\" : \"list\";\n\n const { data: documents, pagination } = await this.repository[listMethod](data);\n\n responseDocument[this.recordsListName] = documents;\n\n if (pagination) {\n responseDocument.pagination = pagination;\n }\n\n return response.success(responseDocument);\n } catch (error: any) {\n log.error(\"restful\", \"list\", error);\n return response.serverError(error);\n }\n }\n\n /**\n * Get single record\n */\n public async get({ request, response }: HttpContext) {\n try {\n if (await this.callMiddleware(\"get\", request, response)) return;\n\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound();\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"get\", error);\n }\n }\n\n /**\n * Create a new record\n */\n public async create({ request, response }: HttpContext) {\n try {\n const model = this.repository.newModel();\n const beforeCreate = await this.beforeCreate(request, response, model);\n\n if (beforeCreate) {\n return beforeCreate;\n }\n\n const beforeSave = await this.beforeSave(request, response, model);\n\n if (beforeSave) {\n return beforeSave;\n }\n\n const record = await this.repository.create(request.all());\n\n const createOutput = await this.onCreate(request, response, record);\n\n if (createOutput) {\n return createOutput;\n }\n\n const saveOutput = await this.onSave(request, response, record);\n\n if (saveOutput) {\n return saveOutput;\n }\n\n if (this.returnOn.create === \"records\") {\n return this.list({ request, response });\n }\n\n return response.successCreate({\n [this.recordName]: record,\n });\n } catch (error: Error | any) {\n log.error(\"restful\", \"create\", error);\n\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Update record\n */\n public async update({ request, response }: HttpContext) {\n try {\n // Find record\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound({\n error: \"Record not found\",\n });\n }\n\n const beforeOutput = await this.beforeUpdate(request, response, record);\n if (beforeOutput) {\n return beforeOutput;\n }\n\n const beforeSafe = await this.beforeSave(request, response, record);\n\n if (beforeSafe) {\n return beforeSafe;\n }\n\n const oldRecord = record.clone();\n\n await record.save(request.allExceptParams());\n\n this.onUpdate(request, response, record, oldRecord);\n this.onSave(request, response, record, oldRecord);\n\n if (this.returnOn.update === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"update\", error);\n }\n }\n\n /**\n * Patch record\n */\n public async patch({ request, response }: HttpContext) {\n try {\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound({\n error: \"Record not found\",\n });\n }\n\n const oldRecord = record.clone();\n\n await this.beforePatch(request, response, record, oldRecord);\n await this.beforeSave(request, response, record, oldRecord);\n\n await record.save(request.heavyExceptParams());\n\n this.onPatch(request, response, record, oldRecord);\n this.onSave(request, response, record, oldRecord);\n\n if (this.returnOn.patch === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"patch\", error);\n }\n }\n\n /**\n * Delete record\n */\n public async delete({ request, response }: HttpContext) {\n try {\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound();\n }\n\n if (await this.callMiddleware(\"delete\", request, response, record)) return;\n\n await this.beforeDelete(request, response, record);\n\n await record.destroy();\n\n this.onDelete(request, response, record);\n\n if (this.returnOn.delete === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success();\n } catch (error: Error | any) {\n log.error(\"restful\", \"delete\", error);\n\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Bulk delete records\n */\n public async bulkDelete({ request, response }: HttpContext) {\n try {\n const ids = request.input(\"id\");\n\n if (!Array.isArray(ids)) {\n return response.badRequest({\n error: \"id must be an array\",\n });\n }\n\n const records = await this.repository.all({\n perform: (query: QueryBuilderContract<T>) =>\n query.whereIn(\n \"id\",\n ids.map((id) => parseInt(id)),\n ),\n });\n\n await Promise.all(\n records.map(async (record) => {\n if (await this.callMiddleware(\"delete\", request, response, record)) {\n return;\n }\n\n await this.beforeDelete(request, response, record);\n await record.destroy();\n this.onDelete(request, response, record);\n }),\n );\n\n if (this.returnOn.delete === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n deleted: records.length,\n });\n } catch (error: Error | any) {\n log.error(\"restful\", \"bulkDelete\", error);\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Before create\n */\n protected async beforeCreate(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * On create\n */\n protected async onCreate(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * Before update\n */\n protected async beforeUpdate(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On update\n */\n protected async onUpdate(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Before delete\n */\n protected async beforeDelete(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * On delete\n */\n protected async onDelete(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * Before patch\n */\n protected async beforePatch(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On patch\n */\n protected async onPatch(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Before save\n */\n protected async beforeSave(\n _request: Request,\n _response: Response,\n _record?: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On save\n */\n protected async onSave(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Call middleware for the given method\n *\n */\n protected async callMiddleware(\n method: string,\n request: Request,\n response: Response,\n _record?: any,\n ) {\n if (!this.middleware[method]) return;\n\n for (const middleware of this.middleware[method]) {\n const output = await middleware({ request, response });\n\n if (output) {\n return output;\n }\n }\n\n return;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region ../core/src/router/describe-route-for-log.ts
|
|
2
|
+
/**
|
|
3
|
+
* The `[GET /products]` label that identifies a route in the request log.
|
|
4
|
+
*
|
|
5
|
+
* A trailing `/*` is stripped because a prefixed route is more readable as the
|
|
6
|
+
* prefix it serves — `/products/*` is `/products` to anyone reading a log.
|
|
7
|
+
*
|
|
8
|
+
* Two things the previous one-liner (`path.replace("/*", "")`) got wrong:
|
|
9
|
+
*
|
|
10
|
+
* 1. The BARE catch-all `/*` became the empty string, so every unmatched
|
|
11
|
+
* request logged as `[GET ]`. The reader is looking for which route
|
|
12
|
+
* matched; a blank is the one answer that helps nobody.
|
|
13
|
+
* 2. `replace` removes the FIRST occurrence wherever it sits, so a `/*` in the
|
|
14
|
+
* middle of a path would have been silently deleted from the label. Nothing
|
|
15
|
+
* declares such a route today — which is exactly why it would go unnoticed
|
|
16
|
+
* if something did.
|
|
17
|
+
*/
|
|
18
|
+
function describeRouteForLog(method, path) {
|
|
19
|
+
return `${method} ${stripTrailingWildcard(path)}`;
|
|
20
|
+
}
|
|
21
|
+
function stripTrailingWildcard(path) {
|
|
22
|
+
if (!path.endsWith("/*")) return path;
|
|
23
|
+
const withoutWildcard = path.slice(0, -2);
|
|
24
|
+
return withoutWildcard.length > 0 ? withoutWildcard : path;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { describeRouteForLog };
|
|
29
|
+
//# sourceMappingURL=describe-route-for-log.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe-route-for-log.mjs","names":[],"sources":["../../../../../../../core/src/router/describe-route-for-log.ts"],"sourcesContent":["/**\n * The `[GET /products]` label that identifies a route in the request log.\n *\n * A trailing `/*` is stripped because a prefixed route is more readable as the\n * prefix it serves — `/products/*` is `/products` to anyone reading a log.\n *\n * Two things the previous one-liner (`path.replace(\"/*\", \"\")`) got wrong:\n *\n * 1. The BARE catch-all `/*` became the empty string, so every unmatched\n * request logged as `[GET ]`. The reader is looking for which route\n * matched; a blank is the one answer that helps nobody.\n * 2. `replace` removes the FIRST occurrence wherever it sits, so a `/*` in the\n * middle of a path would have been silently deleted from the label. Nothing\n * declares such a route today — which is exactly why it would go unnoticed\n * if something did.\n */\nexport function describeRouteForLog(method: string, path: string): string {\n return `${method} ${stripTrailingWildcard(path)}`;\n}\n\nfunction stripTrailingWildcard(path: string): string {\n if (!path.endsWith(\"/*\")) return path;\n\n const withoutWildcard = path.slice(0, -\"/*\".length);\n\n // `/*` alone leaves nothing behind. Show the route's own declared path\n // instead of a blank.\n return withoutWildcard.length > 0 ? withoutWildcard : path;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAgB,oBAAoB,QAAgB,MAAsB;CACxE,OAAO,GAAG,OAAO,GAAG,sBAAsB,IAAI;AAChD;AAEA,SAAS,sBAAsB,MAAsB;CACnD,IAAI,CAAC,KAAK,SAAS,IAAI,GAAG,OAAO;CAEjC,MAAM,kBAAkB,KAAK,MAAM,GAAG,EAAY;CAIlD,OAAO,gBAAgB,SAAS,IAAI,kBAAkB;AACxD"}
|
package/esm/router/index.d.mts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import { GroupedRoutesOptions, Middleware, MiddlewareResponse, PartialPick, RequestControllerContract, RequestHandler, RequestHandlerType, RequestHandlerValidation, RequestMethod, ResourceMethod, RestfulMiddleware, Route, RouteOptions, RouteResource, RouterGroupCallback, RouterStacks } from "./types.mjs";
|
|
1
|
+
import { GroupedRoutesOptions, HttpContext, Middleware, MiddlewareResponse, PartialPick, RequestControllerContract, RequestHandler, RequestHandlerType, RequestHandlerValidation, RequestMethod, ResourceMethod, RestfulMiddleware, Route, RouteOptions, RouteResource, RouterGroupCallback, RouterStacks } from "./types.mjs";
|
|
2
|
+
import { PositionalHandlerSuspect, clearPositionalHandlerSuspects, describePositionalHandlerSuspect, forgetPositionalHandlerSuspects, inspectHandlerSignature, listPositionalHandlerSuspects, looksLikePositionalHandler, reportPositionalHandlerSuspects } from "./positional-handler-diagnostics.mjs";
|
|
3
|
+
import { RouteRegistry } from "./route-registry.mjs";
|
|
2
4
|
import { Router, router } from "./router.mjs";
|
package/esm/router/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { clearPositionalHandlerSuspects, describePositionalHandlerSuspect, forgetPositionalHandlerSuspects, inspectHandlerSignature, listPositionalHandlerSuspects, looksLikePositionalHandler, reportPositionalHandlerSuspects } from "./positional-handler-diagnostics.mjs";
|
|
2
|
+
import { RouteRegistry } from "./route-registry.mjs";
|
|
1
3
|
import { Router, router } from "./router.mjs";
|
|
2
4
|
|
|
3
5
|
export { };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region ../core/src/router/log-request-lifecycle.ts
|
|
2
|
+
function levelForStatus(statusCode) {
|
|
3
|
+
if (statusCode === void 0) return "info";
|
|
4
|
+
if (statusCode >= 500) return "error";
|
|
5
|
+
if (statusCode >= 400) return "warn";
|
|
6
|
+
return "info";
|
|
7
|
+
}
|
|
8
|
+
async function logRequestLifecycle(ports, descriptor, run) {
|
|
9
|
+
const startedAt = ports.now();
|
|
10
|
+
ports.info({
|
|
11
|
+
module: descriptor.module,
|
|
12
|
+
action: descriptor.action,
|
|
13
|
+
message: `Starting Request: ${descriptor.requestId}`,
|
|
14
|
+
context: descriptor.context
|
|
15
|
+
});
|
|
16
|
+
const finish = (level, outcome) => {
|
|
17
|
+
const duration = Math.round(ports.now() - startedAt);
|
|
18
|
+
ports[level]({
|
|
19
|
+
module: descriptor.module,
|
|
20
|
+
action: descriptor.action,
|
|
21
|
+
message: `Completed Request: ${descriptor.requestId} — ${outcome} in ${duration}ms`,
|
|
22
|
+
context: descriptor.context
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
try {
|
|
26
|
+
const result = await run();
|
|
27
|
+
const statusCode = descriptor.statusCode();
|
|
28
|
+
finish(levelForStatus(statusCode), statusCode === void 0 ? "no status" : String(statusCode));
|
|
29
|
+
return result;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
finish("error", `threw ${error instanceof Error ? error.name : typeof error}`);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { logRequestLifecycle };
|
|
38
|
+
//# sourceMappingURL=log-request-lifecycle.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-request-lifecycle.mjs","names":[],"sources":["../../../../../../../core/src/router/log-request-lifecycle.ts"],"sourcesContent":["/**\n * Both ends of a request's log entry.\n *\n * ## Why the completion line exists\n *\n * The router logged `Starting Request: <id>` and nothing else. A request that\n * hung for thirty seconds and one that returned in two milliseconds produced\n * **byte-identical output**, so the log could not answer the one question\n * anyone opens it to ask.\n *\n * Note what \"detecting a hang\" actually means. Nothing can log the completion\n * of a request that never completes — the mechanism is the **absence** of a\n * completion line beside a start line that has one. That only works if the\n * completion line is emitted on every settled path, **including a throw**,\n * which is why the error path here is not an afterthought.\n *\n * ## Why the level tracks the status\n *\n * A handled 500 does not throw; it returns a response. Logging it at `info`\n * would bury the exact entries someone scanning the log is hunting for, so the\n * level is derived from the outcome: informational below 400, a warning for\n * client errors, an error for server errors and for anything that threw.\n *\n * ## Ports rather than a direct `log` import\n *\n * The behaviour worth pinning down is *which lines come out, in which order,\n * at which level, when the run throws*. Injecting the sink and the clock makes\n * that assertable without a server, a socket or a fake timer.\n */\n\nexport type RequestLogEntry = {\n module: string;\n action: string;\n message: string;\n context?: unknown;\n};\n\nexport type RequestLogPorts = {\n info(entry: RequestLogEntry): void;\n warn(entry: RequestLogEntry): void;\n error(entry: RequestLogEntry): void;\n /** Monotonic milliseconds. Called exactly twice per request. */\n now(): number;\n};\n\nexport type RequestLogDescriptor = {\n module: string;\n action: string;\n requestId: string;\n context?: unknown;\n /**\n * Read AFTER the run settles — the status is decided during the request, so\n * capturing it up front would report the default on every entry.\n */\n statusCode(): number | undefined;\n};\n\ntype Level = \"info\" | \"warn\" | \"error\";\n\nfunction levelForStatus(statusCode: number | undefined): Level {\n // An unknown status is not evidence of a problem; saying \"error\" because we\n // failed to read a number would be inventing a defect.\n if (statusCode === undefined) return \"info\";\n\n if (statusCode >= 500) return \"error\";\n\n if (statusCode >= 400) return \"warn\";\n\n return \"info\";\n}\n\nexport async function logRequestLifecycle<TResult>(\n ports: RequestLogPorts,\n descriptor: RequestLogDescriptor,\n run: () => Promise<TResult>,\n): Promise<TResult> {\n const startedAt = ports.now();\n\n ports.info({\n module: descriptor.module,\n action: descriptor.action,\n message: `Starting Request: ${descriptor.requestId}`,\n context: descriptor.context,\n });\n\n const finish = (level: Level, outcome: string): void => {\n const duration = Math.round(ports.now() - startedAt);\n\n ports[level]({\n module: descriptor.module,\n action: descriptor.action,\n message: `Completed Request: ${descriptor.requestId} — ${outcome} in ${duration}ms`,\n context: descriptor.context,\n });\n };\n\n try {\n const result = await run();\n\n const statusCode = descriptor.statusCode();\n\n finish(levelForStatus(statusCode), statusCode === undefined ? \"no status\" : String(statusCode));\n\n return result;\n } catch (error) {\n // The throw is reported and then re-thrown untouched: this function\n // observes the request, it does not own its error handling.\n finish(\"error\", `threw ${error instanceof Error ? error.name : typeof error}`);\n\n throw error;\n }\n}\n"],"mappings":";AA2DA,SAAS,eAAe,YAAuC;CAG7D,IAAI,eAAe,QAAW,OAAO;CAErC,IAAI,cAAc,KAAK,OAAO;CAE9B,IAAI,cAAc,KAAK,OAAO;CAE9B,OAAO;AACT;AAEA,eAAsB,oBACpB,OACA,YACA,KACkB;CAClB,MAAM,YAAY,MAAM,IAAI;CAE5B,MAAM,KAAK;EACT,QAAQ,WAAW;EACnB,QAAQ,WAAW;EACnB,SAAS,qBAAqB,WAAW;EACzC,SAAS,WAAW;CACtB,CAAC;CAED,MAAM,UAAU,OAAc,YAA0B;EACtD,MAAM,WAAW,KAAK,MAAM,MAAM,IAAI,IAAI,SAAS;EAEnD,MAAM,MAAM,CAAC;GACX,QAAQ,WAAW;GACnB,QAAQ,WAAW;GACnB,SAAS,sBAAsB,WAAW,UAAU,KAAK,QAAQ,MAAM,SAAS;GAChF,SAAS,WAAW;EACtB,CAAC;CACH;CAEA,IAAI;EACF,MAAM,SAAS,MAAM,IAAI;EAEzB,MAAM,aAAa,WAAW,WAAW;EAEzC,OAAO,eAAe,UAAU,GAAG,eAAe,SAAY,cAAc,OAAO,UAAU,CAAC;EAE9F,OAAO;CACT,SAAS,OAAO;EAGd,OAAO,SAAS,SAAS,iBAAiB,QAAQ,MAAM,OAAO,OAAO,OAAO;EAE7E,MAAM;CACR;AACF"}
|