@open-mercato/shared 0.6.7 → 0.6.8-canary.320.1.bf889f49e4
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +62 -6
- package/build.mjs +2 -4
- package/dist/lib/ai/safety-identifier.js +32 -0
- package/dist/lib/ai/safety-identifier.js.map +7 -0
- package/dist/lib/auth/organizationScope.js +34 -0
- package/dist/lib/auth/organizationScope.js.map +7 -0
- package/dist/lib/auth/server.js +15 -2
- package/dist/lib/auth/server.js.map +2 -2
- package/dist/lib/bootstrap/clientOnlyModules.js +55 -0
- package/dist/lib/bootstrap/clientOnlyModules.js.map +7 -0
- package/dist/lib/bootstrap/dynamicLoader.js +190 -70
- package/dist/lib/bootstrap/dynamicLoader.js.map +2 -2
- package/dist/lib/bootstrap/factory.js +18 -13
- package/dist/lib/bootstrap/factory.js.map +2 -2
- package/dist/lib/commands/command-bus.js +9 -3
- package/dist/lib/commands/command-bus.js.map +2 -2
- package/dist/lib/commands/command-interceptor-runner.js +9 -4
- package/dist/lib/commands/command-interceptor-runner.js.map +2 -2
- package/dist/lib/commands/errors.js +25 -4
- package/dist/lib/commands/errors.js.map +2 -2
- package/dist/lib/commands/index.js +7 -1
- package/dist/lib/commands/index.js.map +2 -2
- package/dist/lib/crud/enricher-runner.js +2 -11
- package/dist/lib/crud/enricher-runner.js.map +2 -2
- package/dist/lib/crud/factory.js +104 -23
- package/dist/lib/crud/factory.js.map +2 -2
- package/dist/lib/crud/interceptor-runner.js +2 -2
- package/dist/lib/crud/interceptor-runner.js.map +2 -2
- package/dist/lib/crud/mutation-guard-registry.js +2 -2
- package/dist/lib/crud/mutation-guard-registry.js.map +2 -2
- package/dist/lib/crud/types.js +4 -0
- package/dist/lib/crud/types.js.map +3 -3
- package/dist/lib/data/consistency.js +19 -0
- package/dist/lib/data/consistency.js.map +7 -0
- package/dist/lib/data/engine.js +37 -11
- package/dist/lib/data/engine.js.map +2 -2
- package/dist/lib/db/entityMetadata.js +26 -0
- package/dist/lib/db/entityMetadata.js.map +7 -0
- package/dist/lib/db/pg-errors.js +42 -0
- package/dist/lib/db/pg-errors.js.map +2 -2
- package/dist/lib/di/container.js +48 -6
- package/dist/lib/di/container.js.map +2 -2
- package/dist/lib/email/send.js +56 -0
- package/dist/lib/email/send.js.map +2 -2
- package/dist/lib/encryption/find.js +22 -0
- package/dist/lib/encryption/find.js.map +2 -2
- package/dist/lib/encryption/likeFilterWarning.js +66 -0
- package/dist/lib/encryption/likeFilterWarning.js.map +7 -0
- package/dist/lib/encryption/subscriber.js +2 -10
- package/dist/lib/encryption/subscriber.js.map +2 -2
- package/dist/lib/encryption/tenantDataEncryptionService.js +52 -17
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +2 -2
- package/dist/lib/i18n/config.js +1 -1
- package/dist/lib/i18n/config.js.map +2 -2
- package/dist/lib/i18n/dictionary-cache.js +5 -0
- package/dist/lib/i18n/dictionary-cache.js.map +2 -2
- package/dist/lib/i18n/server.js +4 -4
- package/dist/lib/i18n/server.js.map +3 -3
- package/dist/lib/logger/extension.js +29 -0
- package/dist/lib/logger/extension.js.map +7 -0
- package/dist/lib/logger/index.js +47 -3
- package/dist/lib/logger/index.js.map +3 -3
- package/dist/lib/modules/registry.js +36 -6
- package/dist/lib/modules/registry.js.map +2 -2
- package/dist/lib/modules/surfaceFingerprint.js +47 -0
- package/dist/lib/modules/surfaceFingerprint.js.map +7 -0
- package/dist/lib/query/ciphertext-search-warning.js +51 -0
- package/dist/lib/query/ciphertext-search-warning.js.map +7 -0
- package/dist/lib/query/engine.js +60 -43
- package/dist/lib/query/engine.js.map +2 -2
- package/dist/lib/query/types.js.map +1 -1
- package/dist/lib/redis/connection.js +7 -2
- package/dist/lib/redis/connection.js.map +2 -2
- package/dist/lib/search/auto-indexing.js +14 -0
- package/dist/lib/search/auto-indexing.js.map +7 -0
- package/dist/lib/search/availability.js +107 -0
- package/dist/lib/search/availability.js.map +7 -0
- package/dist/lib/search/config.js +61 -2
- package/dist/lib/search/config.js.map +2 -2
- package/dist/lib/search/tokenLookup.js +46 -0
- package/dist/lib/search/tokenLookup.js.map +7 -0
- package/dist/lib/search/tokenize.js +23 -11
- package/dist/lib/search/tokenize.js.map +2 -2
- package/dist/lib/telemetry/runtime.js +35 -0
- package/dist/lib/telemetry/runtime.js.map +7 -0
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/webhooks/body.js +57 -0
- package/dist/lib/webhooks/body.js.map +7 -0
- package/dist/lib/webhooks/inbound-types.js +1 -0
- package/dist/lib/webhooks/inbound-types.js.map +7 -0
- package/dist/lib/webhooks/index.js +13 -1
- package/dist/lib/webhooks/index.js.map +2 -2
- package/dist/lib/webhooks/verify.js +12 -7
- package/dist/lib/webhooks/verify.js.map +2 -2
- package/dist/modules/analytics.js.map +2 -2
- package/dist/modules/overrides.js +50 -1
- package/dist/modules/overrides.js.map +2 -2
- package/dist/modules/payment_gateways/types.js +8 -1
- package/dist/modules/payment_gateways/types.js.map +2 -2
- package/dist/modules/registry.js +1 -0
- package/dist/modules/registry.js.map +2 -2
- package/dist/modules/widgets/extension-points.js +96 -0
- package/dist/modules/widgets/extension-points.js.map +7 -0
- package/dist/security/enabledModulesRegistry.js +56 -11
- package/dist/security/enabledModulesRegistry.js.map +2 -2
- package/dist/security/featurePolicy.js +62 -0
- package/dist/security/featurePolicy.js.map +7 -0
- package/jest.config.cjs +20 -10
- package/package.json +19 -9
- package/scripts/versionSource.cjs +65 -0
- package/src/lib/__tests__/versionSource.test.ts +64 -0
- package/src/lib/ai/__tests__/llm-provider-contract.test.ts +59 -0
- package/src/lib/ai/__tests__/safety-identifier.test.ts +71 -0
- package/src/lib/ai/llm-provider.ts +33 -0
- package/src/lib/ai/safety-identifier.ts +73 -0
- package/src/lib/auth/__tests__/organizationScope.test.ts +107 -0
- package/src/lib/auth/__tests__/server.test.ts +23 -0
- package/src/lib/auth/organizationScope.ts +65 -0
- package/src/lib/auth/server.ts +23 -2
- package/src/lib/bootstrap/__tests__/clientOnlyModules.test.ts +189 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.aliasResolver.test.ts +98 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.appDi.test.ts +211 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.commandInterceptors.test.ts +63 -8
- package/src/lib/bootstrap/__tests__/dynamicLoader.generatedCacheRecovery.test.ts +256 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.test.ts +126 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.tsconfig.test.ts +21 -1
- package/src/lib/bootstrap/__tests__/factory.test.ts +78 -0
- package/src/lib/bootstrap/clientOnlyModules.ts +85 -0
- package/src/lib/bootstrap/dynamicLoader.ts +346 -96
- package/src/lib/bootstrap/factory.ts +21 -15
- package/src/lib/bootstrap/types.ts +6 -0
- package/src/lib/commands/__tests__/command-bus.test.ts +63 -0
- package/src/lib/commands/__tests__/command-interceptor-error.test.ts +91 -1
- package/src/lib/commands/__tests__/command-interceptor-runner.test.ts +101 -0
- package/src/lib/commands/command-bus.ts +9 -3
- package/src/lib/commands/command-interceptor-runner.ts +27 -6
- package/src/lib/commands/command-interceptor.ts +8 -0
- package/src/lib/commands/errors.ts +67 -2
- package/src/lib/commands/index.ts +7 -1
- package/src/lib/crud/__tests__/crud-factory.cache-user-scope.test.ts +171 -0
- package/src/lib/crud/__tests__/crud-factory.test.ts +564 -0
- package/src/lib/crud/__tests__/mutation-guard-registry.test.ts +26 -0
- package/src/lib/crud/enricher-runner.ts +2 -11
- package/src/lib/crud/factory.ts +157 -22
- package/src/lib/crud/interceptor-runner.ts +2 -2
- package/src/lib/crud/mutation-guard-registry.ts +2 -2
- package/src/lib/crud/types.ts +3 -0
- package/src/lib/data/__tests__/consistency.test.ts +38 -0
- package/src/lib/data/__tests__/engine.bulk-suppress.test.ts +18 -3
- package/src/lib/data/consistency.ts +17 -0
- package/src/lib/data/engine.ts +50 -16
- package/src/lib/db/__tests__/entityMetadata.test.ts +38 -0
- package/src/lib/db/__tests__/pg-errors.test.ts +48 -0
- package/src/lib/db/entityMetadata.ts +43 -0
- package/src/lib/db/pg-errors.ts +58 -0
- package/src/lib/di/__tests__/container-app-di-absent.test.ts +155 -0
- package/src/lib/di/__tests__/container-app-di.test.ts +173 -0
- package/src/lib/di/__tests__/registrar-error-log.test.ts +154 -0
- package/src/lib/di/container.ts +72 -8
- package/src/lib/email/__tests__/send.test.ts +37 -1
- package/src/lib/email/send.ts +79 -0
- package/src/lib/encryption/__tests__/likeFilterWarning.test.ts +167 -0
- package/src/lib/encryption/__tests__/subscriber.test.ts +54 -2
- package/src/lib/encryption/__tests__/tenantDataEncryptionService.test.ts +58 -0
- package/src/lib/encryption/find.ts +22 -0
- package/src/lib/encryption/likeFilterWarning.ts +102 -0
- package/src/lib/encryption/subscriber.ts +2 -14
- package/src/lib/encryption/tenantDataEncryptionService.ts +64 -18
- package/src/lib/i18n/__tests__/config.test.ts +8 -0
- package/src/lib/i18n/__tests__/dictionary-cache.test.ts +23 -0
- package/src/lib/i18n/__tests__/server-dictionary-cache.test.ts +30 -0
- package/src/lib/i18n/__tests__/server-unbootstrapped.test.ts +29 -0
- package/src/lib/i18n/config.ts +2 -2
- package/src/lib/i18n/dictionary-cache.ts +5 -0
- package/src/lib/i18n/server.ts +6 -2
- package/src/lib/logger/__tests__/logger.test.ts +77 -0
- package/src/lib/logger/extension.ts +59 -0
- package/src/lib/logger/index.ts +59 -1
- package/src/lib/modules/__tests__/registry.test.ts +26 -0
- package/src/lib/modules/__tests__/surfaceFingerprint.test.ts +122 -0
- package/src/lib/modules/registry.ts +46 -7
- package/src/lib/modules/surfaceFingerprint.ts +87 -0
- package/src/lib/query/__tests__/ciphertext-search-warning.test.ts +178 -0
- package/src/lib/query/__tests__/engine.scope-and-or.test.ts +67 -0
- package/src/lib/query/__tests__/engine.test.ts +213 -13
- package/src/lib/query/ciphertext-search-warning.ts +106 -0
- package/src/lib/query/engine.ts +102 -61
- package/src/lib/query/types.ts +4 -5
- package/src/lib/redis/__tests__/connection.test.ts +16 -0
- package/src/lib/redis/connection.ts +10 -7
- package/src/lib/search/__tests__/availability.test.ts +211 -0
- package/src/lib/search/__tests__/config.test.ts +177 -0
- package/src/lib/search/__tests__/tokenLookup.test.ts +206 -0
- package/src/lib/search/__tests__/tokenize.test.ts +49 -0
- package/src/lib/search/auto-indexing.ts +22 -0
- package/src/lib/search/availability.ts +232 -0
- package/src/lib/search/config.ts +106 -8
- package/src/lib/search/tokenLookup.ts +133 -0
- package/src/lib/search/tokenize.ts +33 -11
- package/src/lib/telemetry/runtime.ts +72 -0
- package/src/lib/webhooks/__tests__/body.test.ts +95 -0
- package/src/lib/webhooks/__tests__/verify.test.ts +20 -1
- package/src/lib/webhooks/body.ts +70 -0
- package/src/lib/webhooks/inbound-types.ts +125 -0
- package/src/lib/webhooks/index.ts +19 -1
- package/src/lib/webhooks/verify.ts +17 -9
- package/src/modules/__tests__/nav-group-order-override.test.ts +183 -0
- package/src/modules/__tests__/registry.test.ts +10 -2
- package/src/modules/analytics.ts +7 -0
- package/src/modules/customer-auth.ts +1 -0
- package/src/modules/encryption.ts +3 -0
- package/src/modules/entities.ts +15 -0
- package/src/modules/navigation/backendChrome.ts +15 -0
- package/src/modules/overrides.ts +103 -0
- package/src/modules/payment_gateways/__tests__/types.test.ts +37 -0
- package/src/modules/payment_gateways/types.ts +41 -0
- package/src/modules/registry.ts +3 -0
- package/src/modules/widgets/__tests__/extension-points.test.ts +101 -0
- package/src/modules/widgets/extension-points.ts +524 -0
- package/src/security/__tests__/featurePolicy.test.ts +166 -0
- package/src/security/enabledModulesRegistry.ts +64 -12
- package/src/security/featurePolicy.ts +89 -0
package/dist/lib/crud/factory.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getAuthFromCookies, getAuthFromRequest } from "@open-mercato/shared/lib
|
|
|
5
5
|
import { SortDir } from "@open-mercato/shared/lib/query/types";
|
|
6
6
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
7
7
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
8
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
8
9
|
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
|
|
9
10
|
import {
|
|
10
11
|
runMutationGuards,
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
isCrudCacheEnabled,
|
|
39
40
|
normalizeIdentifierValue,
|
|
40
41
|
normalizeTagSegment,
|
|
42
|
+
pickFirstIdentifier,
|
|
41
43
|
resolveCrudCache
|
|
42
44
|
} from "./cache.js";
|
|
43
45
|
import { deriveCrudSegmentTag } from "./cache-stats.js";
|
|
@@ -51,11 +53,13 @@ import { parseExtensionHeaders } from "../umes/extension-headers.js";
|
|
|
51
53
|
import { createGenericOptimisticLockReader } from "./optimistic-lock.js";
|
|
52
54
|
import { registerOptimisticLockReaderIfAbsent } from "./optimistic-lock-store.js";
|
|
53
55
|
import { createLogger } from "../logger/index.js";
|
|
56
|
+
import { isTransientDbError } from "../db/pg-errors.js";
|
|
54
57
|
const logger = createLogger("shared").child({ component: "crud" });
|
|
55
|
-
function resolveSortParams(queryParams) {
|
|
56
|
-
const rawSortField = queryParams.sortField ?? queryParams.sort
|
|
57
|
-
const
|
|
58
|
-
const sortField =
|
|
58
|
+
function resolveSortParams(queryParams, defaultSort) {
|
|
59
|
+
const rawSortField = queryParams.sortField ?? queryParams.sort;
|
|
60
|
+
const requestedSortField = typeof rawSortField === "string" && rawSortField.trim().length > 0 ? rawSortField.trim() : null;
|
|
61
|
+
const sortField = requestedSortField ?? defaultSort?.field ?? "id";
|
|
62
|
+
const rawSortDir = queryParams.sortDir ?? queryParams.order ?? (requestedSortField ? "asc" : defaultSort?.dir ?? "asc");
|
|
59
63
|
const normalizedDir = typeof rawSortDir === "string" ? rawSortDir.trim().toLowerCase() : "asc";
|
|
60
64
|
const sortDir = normalizedDir === "desc" ? SortDir.Desc : SortDir.Asc;
|
|
61
65
|
return { sortField, sortDir };
|
|
@@ -95,6 +99,7 @@ const DEFAULT_EXPORT_FORMATS = ["csv", "json", "xml", "markdown"];
|
|
|
95
99
|
const DEFAULT_EXPORT_BATCH_SIZE = 1e3;
|
|
96
100
|
const MIN_EXPORT_BATCH_SIZE = 100;
|
|
97
101
|
const MAX_EXPORT_BATCH_SIZE = 1e4;
|
|
102
|
+
const EXPORT_MAX_PAGES = 1e3;
|
|
98
103
|
function resolveAvailableExportFormats(list) {
|
|
99
104
|
if (!list) return [];
|
|
100
105
|
if (list.export?.enabled === false) return [];
|
|
@@ -143,14 +148,15 @@ function buildExportFromColumns(items, columnsConfig) {
|
|
|
143
148
|
rows
|
|
144
149
|
};
|
|
145
150
|
}
|
|
146
|
-
function buildExportFromCsv(items, csv) {
|
|
151
|
+
function buildExportFromCsv(items, csv, query, ctx) {
|
|
147
152
|
const used = /* @__PURE__ */ new Set();
|
|
148
|
-
const
|
|
153
|
+
const resolvedHeaders = typeof csv.headers === "function" ? csv.headers(query, ctx) : csv.headers;
|
|
154
|
+
const columns = resolvedHeaders.map((header, idx) => ({
|
|
149
155
|
field: sanitizeFieldName(header || `column_${idx + 1}`, used, idx),
|
|
150
156
|
header: header || `Column ${idx + 1}`
|
|
151
157
|
}));
|
|
152
158
|
const rows = items.map((item) => {
|
|
153
|
-
const values = csv.row(item) || [];
|
|
159
|
+
const values = csv.row(item, ctx) || [];
|
|
154
160
|
const row = {};
|
|
155
161
|
columns.forEach((column, idx) => {
|
|
156
162
|
row[column.field] = values[idx];
|
|
@@ -171,12 +177,12 @@ function buildDefaultExport(items) {
|
|
|
171
177
|
rows
|
|
172
178
|
};
|
|
173
179
|
}
|
|
174
|
-
function prepareExportData(items, list) {
|
|
180
|
+
function prepareExportData(items, list, query, ctx) {
|
|
175
181
|
if (list.export?.columns && list.export.columns.length > 0) {
|
|
176
182
|
return buildExportFromColumns(items, list.export.columns);
|
|
177
183
|
}
|
|
178
184
|
if (list.csv) {
|
|
179
|
-
return buildExportFromCsv(items, list.csv);
|
|
185
|
+
return buildExportFromCsv(items, list.csv, query, ctx);
|
|
180
186
|
}
|
|
181
187
|
const prepared = buildDefaultExport(items);
|
|
182
188
|
return {
|
|
@@ -295,7 +301,20 @@ function attachOperationHeader(res, logEntry) {
|
|
|
295
301
|
function handleError(err) {
|
|
296
302
|
if (err instanceof Response) return err;
|
|
297
303
|
if (isCrudHttpError(err)) return json(err.body, { status: err.status });
|
|
304
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
305
|
+
if (interceptorRejection) {
|
|
306
|
+
return json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
307
|
+
}
|
|
298
308
|
if (err instanceof z.ZodError) return json({ error: "Invalid input", details: err.issues }, { status: 400 });
|
|
309
|
+
if (isTransientDbError(err)) {
|
|
310
|
+
logger.warn("Transient DB failure during CRUD handler", {
|
|
311
|
+
message: err instanceof Error ? err.message : void 0
|
|
312
|
+
});
|
|
313
|
+
return json(
|
|
314
|
+
{ error: "Service temporarily unavailable" },
|
|
315
|
+
{ status: 503, headers: { "Retry-After": "2" } }
|
|
316
|
+
);
|
|
317
|
+
}
|
|
299
318
|
const message = err instanceof Error ? err.message : void 0;
|
|
300
319
|
const stack = err instanceof Error ? err.stack : void 0;
|
|
301
320
|
logger.error("Unexpected CRUD error", { message, stack, err });
|
|
@@ -560,6 +579,12 @@ function buildCrudCacheKey(resource, request, ctx, enricherSignature = "") {
|
|
|
560
579
|
`tenant:${normalizeTagSegment(ctx.auth?.tenantId ?? null)}`,
|
|
561
580
|
`selectedOrg:${normalizeTagSegment(ctx.selectedOrganizationId ?? null)}`,
|
|
562
581
|
`scope:${scopeSegment}`,
|
|
582
|
+
// List payloads can vary per caller identity beyond tenant/org scope:
|
|
583
|
+
// buildFilters may narrow by ctx.auth (e.g. ?mine=true), before-interceptor
|
|
584
|
+
// query rewrites are feature-gated per user, and afterList/after-interceptor
|
|
585
|
+
// output is embedded in the stored payload — so entries MUST be partitioned
|
|
586
|
+
// per actor (API key or user), never shared across identities.
|
|
587
|
+
`user:${normalizeTagSegment(ctx.auth?.keyId ?? ctx.auth?.sub ?? null)}`,
|
|
563
588
|
`query:${serializeSearchParams(url.searchParams)}`
|
|
564
589
|
];
|
|
565
590
|
if (enricherSignature) {
|
|
@@ -1149,10 +1174,21 @@ function makeCrudRoute(opts) {
|
|
|
1149
1174
|
profiler.mark("query_engine_prepare");
|
|
1150
1175
|
const qe = ctx.container.resolve("queryEngine");
|
|
1151
1176
|
profiler.mark("query_engine_resolved");
|
|
1152
|
-
const
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1177
|
+
const sortFieldMap = opts.list.sortFieldMap;
|
|
1178
|
+
const resolveSortSelector = (field) => {
|
|
1179
|
+
const mapped = sortFieldMap && sortFieldMap[field] || field;
|
|
1180
|
+
return typeof mapped === "string" ? normalizeSortFieldSelector(mapped) : mapped;
|
|
1181
|
+
};
|
|
1182
|
+
const { sortField: sortFieldRaw, sortDir: sortDirRaw } = resolveSortParams(
|
|
1183
|
+
queryParams,
|
|
1184
|
+
opts.list.defaultSort
|
|
1185
|
+
);
|
|
1186
|
+
const sortField = resolveSortSelector(sortFieldRaw);
|
|
1155
1187
|
const sort = [{ field: sortField, dir: sortDirRaw }];
|
|
1188
|
+
if (opts.list.tiebreakSortField) {
|
|
1189
|
+
const tiebreakField = resolveSortSelector(opts.list.tiebreakSortField);
|
|
1190
|
+
if (tiebreakField !== sortField) sort.push({ field: tiebreakField, dir: SortDir.Asc });
|
|
1191
|
+
}
|
|
1156
1192
|
const page = exportRequested ? { page: 1, pageSize: exportPageSize } : { page: requestedPage, pageSize: requestedPageSize };
|
|
1157
1193
|
const baseFilters = exportFullRequested ? {} : opts.list.buildFilters ? await opts.list.buildFilters(validated, ctx) : {};
|
|
1158
1194
|
const filters = exportFullRequested ? baseFilters : mergeAdvancedFilters(baseFilters, validated);
|
|
@@ -1262,13 +1298,13 @@ function makeCrudRoute(opts) {
|
|
|
1262
1298
|
if (exportRequested && requestedExport) {
|
|
1263
1299
|
const total = typeof res.total === "number" ? res.total : rawItems.length;
|
|
1264
1300
|
const initialExportItems = exportFullRequested ? rawItems.map(normalizeFullRecordForExport) : transformedItems;
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1301
|
+
const exportItems = [...initialExportItems];
|
|
1302
|
+
const exportPageSizeNumber = typeof page.pageSize === "number" ? page.pageSize : exportPageSize;
|
|
1303
|
+
if (rawItems.length >= exportPageSizeNumber) {
|
|
1268
1304
|
const queryBase = { ...queryOpts };
|
|
1269
1305
|
delete queryBase.page;
|
|
1270
1306
|
let nextPage = 2;
|
|
1271
|
-
|
|
1307
|
+
for (; ; ) {
|
|
1272
1308
|
profiler.mark("export_next_page_request", { page: nextPage });
|
|
1273
1309
|
const nextRes = await qe.query(opts.list.entityId, {
|
|
1274
1310
|
...queryBase,
|
|
@@ -1281,11 +1317,14 @@ function makeCrudRoute(opts) {
|
|
|
1281
1317
|
nextTransformed = await decorateItemsWithCustomFields(nextTransformed, ctx, nextRes.customFieldDefinitions);
|
|
1282
1318
|
const nextExportItems = exportFullRequested ? nextItemsRaw.map(normalizeFullRecordForExport) : nextTransformed;
|
|
1283
1319
|
exportItems.push(...nextExportItems);
|
|
1284
|
-
if (
|
|
1320
|
+
if (nextItemsRaw.length < exportPageSizeNumber) break;
|
|
1321
|
+
if (nextPage >= EXPORT_MAX_PAGES) {
|
|
1322
|
+
throw new Error(`[internal] export exceeded ${EXPORT_MAX_PAGES} pages; refusing to return a partial export`);
|
|
1323
|
+
}
|
|
1285
1324
|
nextPage += 1;
|
|
1286
1325
|
}
|
|
1287
1326
|
}
|
|
1288
|
-
const prepared = exportFullRequested ? { columns: ensureColumns(exportItems), rows: exportItems } : prepareExportData(exportItems, opts.list);
|
|
1327
|
+
const prepared = exportFullRequested ? { columns: ensureColumns(exportItems), rows: exportItems } : prepareExportData(exportItems, opts.list, validated, ctx);
|
|
1289
1328
|
const fallbackBase = `${opts.events?.entity || resourceKind || "list"}${exportFullRequested ? "_full" : ""}`;
|
|
1290
1329
|
const filename = finalizeExportFilename(opts.list, requestedExport, fallbackBase);
|
|
1291
1330
|
const serialized = serializeExport(prepared, requestedExport);
|
|
@@ -1469,7 +1508,7 @@ function makeCrudRoute(opts) {
|
|
|
1469
1508
|
profiler.mark("access_logged", accessLogResult);
|
|
1470
1509
|
if (exportRequested && requestedExport) {
|
|
1471
1510
|
const exportItems = exportFullRequested ? list.map(normalizeFullRecordForExport) : list;
|
|
1472
|
-
const prepared = exportFullRequested ? { columns: ensureColumns(exportItems), rows: exportItems } : prepareExportData(exportItems, opts.list);
|
|
1511
|
+
const prepared = exportFullRequested ? { columns: ensureColumns(exportItems), rows: exportItems } : prepareExportData(exportItems, opts.list, validated, ctx);
|
|
1473
1512
|
const fallbackBase = `${opts.events?.entity || resourceKind || "list"}${exportFullRequested ? "_full" : ""}`;
|
|
1474
1513
|
const filename = finalizeExportFilename(opts.list, requestedExport, fallbackBase);
|
|
1475
1514
|
const serialized = serializeExport(prepared, requestedExport);
|
|
@@ -1589,6 +1628,29 @@ function makeCrudRoute(opts) {
|
|
|
1589
1628
|
}
|
|
1590
1629
|
}
|
|
1591
1630
|
}
|
|
1631
|
+
const createCmdUserFeatures = await resolveUserFeatures(ctx);
|
|
1632
|
+
const { allGuards: createCmdAllGuards } = collectAndRunGuards(ctx.container);
|
|
1633
|
+
let createCmdGuardAfterCallbacks = [];
|
|
1634
|
+
if (createCmdAllGuards.length && ctx.auth.tenantId) {
|
|
1635
|
+
const guardResult = await runMutationGuards(createCmdAllGuards, {
|
|
1636
|
+
tenantId: ctx.auth.tenantId,
|
|
1637
|
+
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId ?? null,
|
|
1638
|
+
userId: ctx.auth.sub,
|
|
1639
|
+
resourceKind,
|
|
1640
|
+
resourceId: null,
|
|
1641
|
+
operation: "create",
|
|
1642
|
+
requestMethod: request.method,
|
|
1643
|
+
requestHeaders: request.headers,
|
|
1644
|
+
mutationPayload: input2 && typeof input2 === "object" ? input2 : null
|
|
1645
|
+
}, { userFeatures: createCmdUserFeatures ?? [] });
|
|
1646
|
+
if (!guardResult.ok) {
|
|
1647
|
+
return json(guardResult.errorBody ?? { error: "Operation blocked by guard" }, { status: guardResult.errorStatus ?? 422 });
|
|
1648
|
+
}
|
|
1649
|
+
if (guardResult.modifiedPayload && typeof input2 === "object" && input2) {
|
|
1650
|
+
input2 = { ...input2, ...guardResult.modifiedPayload };
|
|
1651
|
+
}
|
|
1652
|
+
createCmdGuardAfterCallbacks = guardResult.afterSuccessCallbacks;
|
|
1653
|
+
}
|
|
1592
1654
|
const baseMetadata = {
|
|
1593
1655
|
tenantId: ctx.auth?.tenantId ?? null,
|
|
1594
1656
|
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId ?? null,
|
|
@@ -1628,6 +1690,22 @@ function makeCrudRoute(opts) {
|
|
|
1628
1690
|
const status = action.status ?? 201;
|
|
1629
1691
|
const response = json(resolvedPayload, { status });
|
|
1630
1692
|
attachOperationHeader(response, logEntry);
|
|
1693
|
+
const commandResultId = pickFirstIdentifier(
|
|
1694
|
+
result?.id,
|
|
1695
|
+
resolvedPayload?.id
|
|
1696
|
+
);
|
|
1697
|
+
if (createCmdGuardAfterCallbacks.length && ctx.auth.tenantId && commandResultId) {
|
|
1698
|
+
await runGuardAfterSuccessCallbacks(createCmdGuardAfterCallbacks, {
|
|
1699
|
+
tenantId: ctx.auth.tenantId,
|
|
1700
|
+
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId ?? null,
|
|
1701
|
+
userId: ctx.auth.sub,
|
|
1702
|
+
resourceKind,
|
|
1703
|
+
resourceId: commandResultId,
|
|
1704
|
+
operation: "create",
|
|
1705
|
+
requestMethod: request.method,
|
|
1706
|
+
requestHeaders: request.headers
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1631
1709
|
return response;
|
|
1632
1710
|
}
|
|
1633
1711
|
const createConfig = opts.create;
|
|
@@ -1714,7 +1792,8 @@ function makeCrudRoute(opts) {
|
|
|
1714
1792
|
recordId: String(created[ormCfg.idField]),
|
|
1715
1793
|
organizationId: targetOrgId,
|
|
1716
1794
|
tenantId: writeTenantId,
|
|
1717
|
-
values
|
|
1795
|
+
values,
|
|
1796
|
+
notify: false
|
|
1718
1797
|
});
|
|
1719
1798
|
}
|
|
1720
1799
|
}
|
|
@@ -2017,7 +2096,8 @@ function makeCrudRoute(opts) {
|
|
|
2017
2096
|
recordId: String(updated[ormCfg.idField]),
|
|
2018
2097
|
organizationId: targetOrgId,
|
|
2019
2098
|
tenantId: writeTenantId,
|
|
2020
|
-
values
|
|
2099
|
+
values,
|
|
2100
|
+
notify: false
|
|
2021
2101
|
});
|
|
2022
2102
|
}
|
|
2023
2103
|
}
|
|
@@ -2113,7 +2193,8 @@ function makeCrudRoute(opts) {
|
|
|
2113
2193
|
ctx,
|
|
2114
2194
|
request,
|
|
2115
2195
|
method: "DELETE",
|
|
2116
|
-
body: interceptorInput
|
|
2196
|
+
body: interceptorInput,
|
|
2197
|
+
query: raw.query
|
|
2117
2198
|
});
|
|
2118
2199
|
if (beforeInterceptors2.errorResponse) return beforeInterceptors2.errorResponse;
|
|
2119
2200
|
interceptorRequestPayload = beforeInterceptors2.requestPayload;
|
|
@@ -2224,7 +2305,7 @@ function makeCrudRoute(opts) {
|
|
|
2224
2305
|
request,
|
|
2225
2306
|
method: "DELETE",
|
|
2226
2307
|
body: idFrom === "query" ? void 0 : { id },
|
|
2227
|
-
query: idFrom === "query" ?
|
|
2308
|
+
query: idFrom === "query" ? Object.fromEntries(url.searchParams.entries()) : void 0
|
|
2228
2309
|
});
|
|
2229
2310
|
if (beforeInterceptors.errorResponse) return beforeInterceptors.errorResponse;
|
|
2230
2311
|
interceptorRequestPayload = beforeInterceptors.requestPayload;
|