@open-mercato/shared 0.4.2-canary-c02407ff85
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/build.mjs +101 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/api/crud.js +47 -0
- package/dist/lib/api/crud.js.map +7 -0
- package/dist/lib/api/scoped.js +140 -0
- package/dist/lib/api/scoped.js.map +7 -0
- package/dist/lib/auth/jwt.js +34 -0
- package/dist/lib/auth/jwt.js.map +7 -0
- package/dist/lib/auth/server.js +157 -0
- package/dist/lib/auth/server.js.map +7 -0
- package/dist/lib/boolean.js +22 -0
- package/dist/lib/boolean.js.map +7 -0
- package/dist/lib/bootstrap/appResolver.js +43 -0
- package/dist/lib/bootstrap/appResolver.js.map +7 -0
- package/dist/lib/bootstrap/dynamicLoader.js +108 -0
- package/dist/lib/bootstrap/dynamicLoader.js.map +7 -0
- package/dist/lib/bootstrap/factory.js +59 -0
- package/dist/lib/bootstrap/factory.js.map +7 -0
- package/dist/lib/bootstrap/index.js +11 -0
- package/dist/lib/bootstrap/index.js.map +7 -0
- package/dist/lib/bootstrap/types.js +1 -0
- package/dist/lib/bootstrap/types.js.map +7 -0
- package/dist/lib/cache/segments.js +36 -0
- package/dist/lib/cache/segments.js.map +7 -0
- package/dist/lib/cli/progress.js +46 -0
- package/dist/lib/cli/progress.js.map +7 -0
- package/dist/lib/commands/command-bus.js +285 -0
- package/dist/lib/commands/command-bus.js.map +7 -0
- package/dist/lib/commands/customFieldSnapshots.js +66 -0
- package/dist/lib/commands/customFieldSnapshots.js.map +7 -0
- package/dist/lib/commands/helpers.js +98 -0
- package/dist/lib/commands/helpers.js.map +7 -0
- package/dist/lib/commands/index.js +8 -0
- package/dist/lib/commands/index.js.map +7 -0
- package/dist/lib/commands/operationMetadata.js +32 -0
- package/dist/lib/commands/operationMetadata.js.map +7 -0
- package/dist/lib/commands/registry.js +43 -0
- package/dist/lib/commands/registry.js.map +7 -0
- package/dist/lib/commands/scope.js +44 -0
- package/dist/lib/commands/scope.js.map +7 -0
- package/dist/lib/commands/types.js +8 -0
- package/dist/lib/commands/types.js.map +7 -0
- package/dist/lib/crud/cache-stats.js +98 -0
- package/dist/lib/crud/cache-stats.js.map +7 -0
- package/dist/lib/crud/cache.js +175 -0
- package/dist/lib/crud/cache.js.map +7 -0
- package/dist/lib/crud/custom-fields-client.js +52 -0
- package/dist/lib/crud/custom-fields-client.js.map +7 -0
- package/dist/lib/crud/custom-fields.js +467 -0
- package/dist/lib/crud/custom-fields.js.map +7 -0
- package/dist/lib/crud/errors.js +24 -0
- package/dist/lib/crud/errors.js.map +7 -0
- package/dist/lib/crud/exporters.js +154 -0
- package/dist/lib/crud/exporters.js.map +7 -0
- package/dist/lib/crud/factory.js +1311 -0
- package/dist/lib/crud/factory.js.map +7 -0
- package/dist/lib/crud/types.js +1 -0
- package/dist/lib/crud/types.js.map +7 -0
- package/dist/lib/custom-fields/normalize.js +36 -0
- package/dist/lib/custom-fields/normalize.js.map +7 -0
- package/dist/lib/data/engine.js +396 -0
- package/dist/lib/data/engine.js.map +7 -0
- package/dist/lib/db/escapeLikePattern.js +5 -0
- package/dist/lib/db/escapeLikePattern.js.map +7 -0
- package/dist/lib/db/mikro.js +82 -0
- package/dist/lib/db/mikro.js.map +7 -0
- package/dist/lib/di/container.js +94 -0
- package/dist/lib/di/container.js.map +7 -0
- package/dist/lib/email/send.js +12 -0
- package/dist/lib/email/send.js.map +7 -0
- package/dist/lib/encryption/aes.js +58 -0
- package/dist/lib/encryption/aes.js.map +7 -0
- package/dist/lib/encryption/customFieldValues.js +49 -0
- package/dist/lib/encryption/customFieldValues.js.map +7 -0
- package/dist/lib/encryption/entityFields.js +26 -0
- package/dist/lib/encryption/entityFields.js.map +7 -0
- package/dist/lib/encryption/entityIds.js +80 -0
- package/dist/lib/encryption/entityIds.js.map +7 -0
- package/dist/lib/encryption/find.js +45 -0
- package/dist/lib/encryption/find.js.map +7 -0
- package/dist/lib/encryption/indexDoc.js +69 -0
- package/dist/lib/encryption/indexDoc.js.map +7 -0
- package/dist/lib/encryption/kms.js +282 -0
- package/dist/lib/encryption/kms.js.map +7 -0
- package/dist/lib/encryption/subscriber.js +330 -0
- package/dist/lib/encryption/subscriber.js.map +7 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js +252 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +7 -0
- package/dist/lib/encryption/toggles.js +18 -0
- package/dist/lib/encryption/toggles.js.map +7 -0
- package/dist/lib/entities/naming.js +9 -0
- package/dist/lib/entities/naming.js.map +7 -0
- package/dist/lib/entities/system-entities.js +43 -0
- package/dist/lib/entities/system-entities.js.map +7 -0
- package/dist/lib/frontend/organizationEvents.js +41 -0
- package/dist/lib/frontend/organizationEvents.js.map +7 -0
- package/dist/lib/frontend/useOrganizationScope.js +32 -0
- package/dist/lib/frontend/useOrganizationScope.js.map +7 -0
- package/dist/lib/hotkeys/index.js +128 -0
- package/dist/lib/hotkeys/index.js.map +7 -0
- package/dist/lib/i18n/app-dictionaries.js +17 -0
- package/dist/lib/i18n/app-dictionaries.js.map +7 -0
- package/dist/lib/i18n/config.js +7 -0
- package/dist/lib/i18n/config.js.map +7 -0
- package/dist/lib/i18n/context.js +50 -0
- package/dist/lib/i18n/context.js.map +7 -0
- package/dist/lib/i18n/server.js +68 -0
- package/dist/lib/i18n/server.js.map +7 -0
- package/dist/lib/i18n/translate.js +45 -0
- package/dist/lib/i18n/translate.js.map +7 -0
- package/dist/lib/indexers/error-log.js +82 -0
- package/dist/lib/indexers/error-log.js.map +7 -0
- package/dist/lib/indexers/status-log.js +80 -0
- package/dist/lib/indexers/status-log.js.map +7 -0
- package/dist/lib/lib/auth/jwt.js +34 -0
- package/dist/lib/lib/auth/jwt.js.map +7 -0
- package/dist/lib/lib/auth/server.js +77 -0
- package/dist/lib/lib/auth/server.js.map +7 -0
- package/dist/lib/lib/email/send.js +12 -0
- package/dist/lib/lib/email/send.js.map +7 -0
- package/dist/lib/lib/i18n/config.js +7 -0
- package/dist/lib/lib/i18n/config.js.map +7 -0
- package/dist/lib/lib/i18n/context.js +31 -0
- package/dist/lib/lib/i18n/context.js.map +7 -0
- package/dist/lib/lib/utils.js +9 -0
- package/dist/lib/lib/utils.js.map +7 -0
- package/dist/lib/location/countries.js +68 -0
- package/dist/lib/location/countries.js.map +7 -0
- package/dist/lib/modules/index.js +6 -0
- package/dist/lib/modules/index.js.map +7 -0
- package/dist/lib/modules/registry.js +18 -0
- package/dist/lib/modules/registry.js.map +7 -0
- package/dist/lib/openapi/crud.js +137 -0
- package/dist/lib/openapi/crud.js.map +7 -0
- package/dist/lib/openapi/generator.js +1131 -0
- package/dist/lib/openapi/generator.js.map +7 -0
- package/dist/lib/openapi/index.js +10 -0
- package/dist/lib/openapi/index.js.map +7 -0
- package/dist/lib/openapi/sanitize.js +110 -0
- package/dist/lib/openapi/sanitize.js.map +7 -0
- package/dist/lib/openapi/types.js +1 -0
- package/dist/lib/openapi/types.js.map +7 -0
- package/dist/lib/profiler/index.js +258 -0
- package/dist/lib/profiler/index.js.map +7 -0
- package/dist/lib/query/engine.js +729 -0
- package/dist/lib/query/engine.js.map +7 -0
- package/dist/lib/query/join-utils.js +195 -0
- package/dist/lib/query/join-utils.js.map +7 -0
- package/dist/lib/query/types.js +9 -0
- package/dist/lib/query/types.js.map +7 -0
- package/dist/lib/search/config.js +32 -0
- package/dist/lib/search/config.js.map +7 -0
- package/dist/lib/search/tokenize.js +34 -0
- package/dist/lib/search/tokenize.js.map +7 -0
- package/dist/lib/slugify.js +24 -0
- package/dist/lib/slugify.js.map +7 -0
- package/dist/lib/testing/bootstrap.js +51 -0
- package/dist/lib/testing/bootstrap.js.map +7 -0
- package/dist/lib/testing/index.js +17 -0
- package/dist/lib/testing/index.js.map +7 -0
- package/dist/lib/testing/renderWithProviders.js +15 -0
- package/dist/lib/testing/renderWithProviders.js.map +7 -0
- package/dist/lib/url.js +12 -0
- package/dist/lib/url.js.map +7 -0
- package/dist/lib/utils.js +13 -0
- package/dist/lib/utils.js.map +7 -0
- package/dist/lib/version.js +7 -0
- package/dist/lib/version.js.map +7 -0
- package/dist/modules/dashboard/widgets.js +1 -0
- package/dist/modules/dashboard/widgets.js.map +7 -0
- package/dist/modules/dsl.js +30 -0
- package/dist/modules/dsl.js.map +7 -0
- package/dist/modules/entities/kinds.js +22 -0
- package/dist/modules/entities/kinds.js.map +7 -0
- package/dist/modules/entities/options.js +26 -0
- package/dist/modules/entities/options.js.map +7 -0
- package/dist/modules/entities/validation.js +102 -0
- package/dist/modules/entities/validation.js.map +7 -0
- package/dist/modules/entities/validators.js +88 -0
- package/dist/modules/entities/validators.js.map +7 -0
- package/dist/modules/entities.js +1 -0
- package/dist/modules/entities.js.map +7 -0
- package/dist/modules/navigation/sidebarPreferences.js +50 -0
- package/dist/modules/navigation/sidebarPreferences.js.map +7 -0
- package/dist/modules/perspectives/types.js +1 -0
- package/dist/modules/perspectives/types.js.map +7 -0
- package/dist/modules/registry.js +96 -0
- package/dist/modules/registry.js.map +7 -0
- package/dist/modules/search.js +15 -0
- package/dist/modules/search.js.map +7 -0
- package/dist/modules/vector.js +1 -0
- package/dist/modules/vector.js.map +7 -0
- package/dist/modules/widgets/injection-loader.js +180 -0
- package/dist/modules/widgets/injection-loader.js.map +7 -0
- package/dist/modules/widgets/injection.js +1 -0
- package/dist/modules/widgets/injection.js.map +7 -0
- package/dist/security/features.js +23 -0
- package/dist/security/features.js.map +7 -0
- package/dist/types/pg.d.js +1 -0
- package/dist/types/pg.d.js.map +7 -0
- package/dist/types/react-email.d.js +1 -0
- package/dist/types/react-email.d.js.map +7 -0
- package/dist/types/resend.d.js +1 -0
- package/dist/types/resend.d.js.map +7 -0
- package/jest.config.cjs +22 -0
- package/package.json +88 -0
- package/src/index.ts +0 -0
- package/src/lib/api/__tests__/scoped.test.ts +38 -0
- package/src/lib/api/crud.ts +59 -0
- package/src/lib/api/scoped.ts +239 -0
- package/src/lib/auth/jwt.ts +39 -0
- package/src/lib/auth/server.ts +199 -0
- package/src/lib/boolean.ts +17 -0
- package/src/lib/bootstrap/appResolver.ts +85 -0
- package/src/lib/bootstrap/dynamicLoader.ts +177 -0
- package/src/lib/bootstrap/factory.ts +108 -0
- package/src/lib/bootstrap/index.ts +23 -0
- package/src/lib/bootstrap/types.ts +31 -0
- package/src/lib/cache/segments.ts +56 -0
- package/src/lib/cli/progress.ts +55 -0
- package/src/lib/commands/__tests__/command-bus.test.ts +84 -0
- package/src/lib/commands/__tests__/helpers.test.ts +42 -0
- package/src/lib/commands/command-bus.ts +349 -0
- package/src/lib/commands/customFieldSnapshots.ts +86 -0
- package/src/lib/commands/helpers.ts +143 -0
- package/src/lib/commands/index.ts +4 -0
- package/src/lib/commands/operationMetadata.ts +40 -0
- package/src/lib/commands/registry.ts +46 -0
- package/src/lib/commands/scope.ts +59 -0
- package/src/lib/commands/types.ts +63 -0
- package/src/lib/crud/__tests__/crud-factory.test.ts +333 -0
- package/src/lib/crud/__tests__/custom-fields.test.ts +150 -0
- package/src/lib/crud/cache-stats.ts +127 -0
- package/src/lib/crud/cache.ts +205 -0
- package/src/lib/crud/custom-fields-client.ts +54 -0
- package/src/lib/crud/custom-fields.ts +607 -0
- package/src/lib/crud/errors.ts +23 -0
- package/src/lib/crud/exporters.ts +188 -0
- package/src/lib/crud/factory.ts +1622 -0
- package/src/lib/crud/types.ts +29 -0
- package/src/lib/custom-fields/normalize.ts +45 -0
- package/src/lib/data/engine.ts +562 -0
- package/src/lib/db/escapeLikePattern.ts +2 -0
- package/src/lib/db/mikro.ts +100 -0
- package/src/lib/di/container.ts +105 -0
- package/src/lib/email/send.ts +18 -0
- package/src/lib/encryption/__tests__/customFieldValues.test.ts +63 -0
- package/src/lib/encryption/__tests__/indexDoc.test.ts +115 -0
- package/src/lib/encryption/aes.ts +64 -0
- package/src/lib/encryption/customFieldValues.ts +67 -0
- package/src/lib/encryption/entityFields.ts +39 -0
- package/src/lib/encryption/entityIds.ts +107 -0
- package/src/lib/encryption/find.ts +81 -0
- package/src/lib/encryption/indexDoc.ts +104 -0
- package/src/lib/encryption/kms.ts +337 -0
- package/src/lib/encryption/subscriber.ts +416 -0
- package/src/lib/encryption/tenantDataEncryptionService.ts +313 -0
- package/src/lib/encryption/toggles.ts +15 -0
- package/src/lib/entities/naming.ts +6 -0
- package/src/lib/entities/system-entities.ts +43 -0
- package/src/lib/frontend/organizationEvents.ts +55 -0
- package/src/lib/frontend/useOrganizationScope.ts +30 -0
- package/src/lib/hotkeys/index.ts +168 -0
- package/src/lib/i18n/app-dictionaries.ts +18 -0
- package/src/lib/i18n/config.ts +4 -0
- package/src/lib/i18n/context.tsx +66 -0
- package/src/lib/i18n/server.ts +74 -0
- package/src/lib/i18n/translate.ts +54 -0
- package/src/lib/indexers/error-log.ts +106 -0
- package/src/lib/indexers/status-log.ts +119 -0
- package/src/lib/lib/auth/jwt.ts +39 -0
- package/src/lib/lib/auth/server.ts +94 -0
- package/src/lib/lib/email/send.ts +18 -0
- package/src/lib/lib/i18n/config.ts +4 -0
- package/src/lib/lib/i18n/context.tsx +38 -0
- package/src/lib/lib/utils.ts +6 -0
- package/src/lib/location/countries.ts +97 -0
- package/src/lib/modules/index.ts +1 -0
- package/src/lib/modules/registry.ts +18 -0
- package/src/lib/openapi/crud.ts +218 -0
- package/src/lib/openapi/generator.ts +1311 -0
- package/src/lib/openapi/index.ts +4 -0
- package/src/lib/openapi/sanitize.ts +137 -0
- package/src/lib/openapi/types.ts +79 -0
- package/src/lib/profiler/index.ts +371 -0
- package/src/lib/query/__tests__/engine.test.ts +274 -0
- package/src/lib/query/engine.ts +837 -0
- package/src/lib/query/join-utils.ts +238 -0
- package/src/lib/query/types.ts +121 -0
- package/src/lib/search/config.ts +49 -0
- package/src/lib/search/tokenize.ts +45 -0
- package/src/lib/slugify.ts +28 -0
- package/src/lib/testing/bootstrap.ts +124 -0
- package/src/lib/testing/index.ts +15 -0
- package/src/lib/testing/renderWithProviders.tsx +31 -0
- package/src/lib/url.ts +12 -0
- package/src/lib/utils.ts +17 -0
- package/src/lib/version.ts +5 -0
- package/src/modules/__tests__/dsl.test.ts +35 -0
- package/src/modules/__tests__/registry.test.ts +300 -0
- package/src/modules/dashboard/widgets.ts +57 -0
- package/src/modules/dsl.ts +32 -0
- package/src/modules/entities/__tests__/validation.test.ts +52 -0
- package/src/modules/entities/kinds.ts +20 -0
- package/src/modules/entities/options.ts +36 -0
- package/src/modules/entities/validation.ts +118 -0
- package/src/modules/entities/validators.ts +93 -0
- package/src/modules/entities.ts +102 -0
- package/src/modules/navigation/sidebarPreferences.ts +62 -0
- package/src/modules/perspectives/types.ts +40 -0
- package/src/modules/registry.ts +249 -0
- package/src/modules/search.ts +325 -0
- package/src/modules/vector.ts +122 -0
- package/src/modules/widgets/__tests__/injection.test.ts +48 -0
- package/src/modules/widgets/injection-loader.ts +235 -0
- package/src/modules/widgets/injection.ts +120 -0
- package/src/security/features.ts +22 -0
- package/src/types/pg.d.ts +2 -0
- package/src/types/react-email.d.ts +2 -0
- package/src/types/resend.d.ts +2 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/commands/registry.ts"],
|
|
4
|
+
"sourcesContent": ["import type { CommandHandler } from './types'\n\nclass CommandRegistry {\n private handlers = new Map<string, CommandHandler>()\n\n register(handler: CommandHandler) {\n if (!handler?.id) throw new Error('Command handler must define an id')\n if (this.handlers.has(handler.id)) {\n throw new Error(`Duplicate command registration for id ${handler.id}`)\n }\n this.handlers.set(handler.id, handler)\n }\n\n unregister(id: string) {\n this.handlers.delete(id)\n }\n\n get<TInput = unknown, TResult = unknown>(id: string): CommandHandler<TInput, TResult> | null {\n return (this.handlers.get(id) as CommandHandler<TInput, TResult> | undefined) ?? null\n }\n\n has(id: string): boolean {\n return this.handlers.has(id)\n }\n\n /**\n * List all registered command handler IDs.\n */\n list(): string[] {\n return Array.from(this.handlers.keys())\n }\n\n clear() {\n this.handlers.clear()\n }\n}\n\nexport const commandRegistry = new CommandRegistry()\n\nexport function registerCommand(handler: CommandHandler) {\n commandRegistry.register(handler)\n}\n\nexport function unregisterCommand(id: string) {\n commandRegistry.unregister(id)\n}\n"],
|
|
5
|
+
"mappings": "AAEA,MAAM,gBAAgB;AAAA,EAAtB;AACE,SAAQ,WAAW,oBAAI,IAA4B;AAAA;AAAA,EAEnD,SAAS,SAAyB;AAChC,QAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,mCAAmC;AACrE,QAAI,KAAK,SAAS,IAAI,QAAQ,EAAE,GAAG;AACjC,YAAM,IAAI,MAAM,yCAAyC,QAAQ,EAAE,EAAE;AAAA,IACvE;AACA,SAAK,SAAS,IAAI,QAAQ,IAAI,OAAO;AAAA,EACvC;AAAA,EAEA,WAAW,IAAY;AACrB,SAAK,SAAS,OAAO,EAAE;AAAA,EACzB;AAAA,EAEA,IAAyC,IAAoD;AAC3F,WAAQ,KAAK,SAAS,IAAI,EAAE,KAAqD;AAAA,EACnF;AAAA,EAEA,IAAI,IAAqB;AACvB,WAAO,KAAK,SAAS,IAAI,EAAE;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAiB;AACf,WAAO,MAAM,KAAK,KAAK,SAAS,KAAK,CAAC;AAAA,EACxC;AAAA,EAEA,QAAQ;AACN,SAAK,SAAS,MAAM;AAAA,EACtB;AACF;AAEO,MAAM,kBAAkB,IAAI,gBAAgB;AAE5C,SAAS,gBAAgB,SAAyB;AACvD,kBAAgB,SAAS,OAAO;AAClC;AAEO,SAAS,kBAAkB,IAAY;AAC5C,kBAAgB,WAAW,EAAE;AAC/B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
2
|
+
import { env } from "process";
|
|
3
|
+
function logScopeViolation(ctx, expected, actual) {
|
|
4
|
+
try {
|
|
5
|
+
const requestInfo = ctx.request && typeof ctx.request === "object" ? {
|
|
6
|
+
method: ctx.request.method ?? void 0,
|
|
7
|
+
url: ctx.request.url ?? void 0
|
|
8
|
+
} : null;
|
|
9
|
+
const scope = ctx.organizationScope ? {
|
|
10
|
+
selectedId: ctx.organizationScope.selectedId ?? null,
|
|
11
|
+
tenantId: ctx.organizationScope.tenantId ?? null,
|
|
12
|
+
allowedIdsCount: Array.isArray(ctx.organizationScope.allowedIds) ? ctx.organizationScope.allowedIds.length : null,
|
|
13
|
+
filterIdsCount: Array.isArray(ctx.organizationScope.filterIds) ? ctx.organizationScope.filterIds.length : null
|
|
14
|
+
} : null;
|
|
15
|
+
if (env.NODE_ENV !== "test") {
|
|
16
|
+
console.warn("[scope] Forbidden organization scope mismatch detected", {
|
|
17
|
+
expectedId: expected,
|
|
18
|
+
actualId: actual,
|
|
19
|
+
userId: ctx.auth?.sub ?? null,
|
|
20
|
+
actorTenantId: ctx.auth?.tenantId ?? null,
|
|
21
|
+
actorOrganizationId: ctx.auth?.orgId ?? null,
|
|
22
|
+
selectedOrganizationId: ctx.selectedOrganizationId ?? null,
|
|
23
|
+
organizationIdsCount: Array.isArray(ctx.organizationIds) ? ctx.organizationIds.length : null,
|
|
24
|
+
scope,
|
|
25
|
+
request: requestInfo
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function ensureOrganizationScope(ctx, organizationId) {
|
|
32
|
+
if (ctx.auth?.isSuperAdmin === true || ctx.organizationScope?.allowedIds === null) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const currentOrg = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null;
|
|
36
|
+
if (currentOrg && currentOrg !== organizationId) {
|
|
37
|
+
logScopeViolation(ctx, organizationId, currentOrg);
|
|
38
|
+
throw new CrudHttpError(403, { error: "Forbidden" });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
ensureOrganizationScope
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/commands/scope.ts"],
|
|
4
|
+
"sourcesContent": ["import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { env } from 'process'\n\nfunction logScopeViolation(\n ctx: CommandRuntimeContext,\n expected: string,\n actual: string | null\n): void {\n try {\n const requestInfo =\n ctx.request && typeof ctx.request === 'object'\n ? {\n method: (ctx.request as Request).method ?? undefined,\n url: (ctx.request as Request).url ?? undefined,\n }\n : null\n const scope = ctx.organizationScope\n ? {\n selectedId: ctx.organizationScope.selectedId ?? null,\n tenantId: ctx.organizationScope.tenantId ?? null,\n allowedIdsCount: Array.isArray(ctx.organizationScope.allowedIds)\n ? ctx.organizationScope.allowedIds.length\n : null,\n filterIdsCount: Array.isArray(ctx.organizationScope.filterIds)\n ? ctx.organizationScope.filterIds.length\n : null,\n }\n : null\n if (env.NODE_ENV !== 'test') {\n console.warn('[scope] Forbidden organization scope mismatch detected', {\n expectedId: expected,\n actualId: actual,\n userId: ctx.auth?.sub ?? null,\n actorTenantId: ctx.auth?.tenantId ?? null,\n actorOrganizationId: ctx.auth?.orgId ?? null,\n selectedOrganizationId: ctx.selectedOrganizationId ?? null,\n organizationIdsCount: Array.isArray(ctx.organizationIds) ? ctx.organizationIds.length : null,\n scope,\n request: requestInfo,\n })\n }\n } catch {\n // best-effort logging\n }\n}\n\nexport function ensureOrganizationScope(ctx: CommandRuntimeContext, organizationId: string): void {\n // Superadmins with global org access can operate on any organization's records\n if (ctx.auth?.isSuperAdmin === true || ctx.organizationScope?.allowedIds === null) {\n return\n }\n\n const currentOrg = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null\n if (currentOrg && currentOrg !== organizationId) {\n logScopeViolation(ctx, organizationId, currentOrg)\n throw new CrudHttpError(403, { error: 'Forbidden' })\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,qBAAqB;AAE9B,SAAS,WAAW;AAEpB,SAAS,kBACP,KACA,UACA,QACM;AACN,MAAI;AACF,UAAM,cACJ,IAAI,WAAW,OAAO,IAAI,YAAY,WAClC;AAAA,MACE,QAAS,IAAI,QAAoB,UAAU;AAAA,MAC3C,KAAM,IAAI,QAAoB,OAAO;AAAA,IACvC,IACA;AACN,UAAM,QAAQ,IAAI,oBACd;AAAA,MACE,YAAY,IAAI,kBAAkB,cAAc;AAAA,MAChD,UAAU,IAAI,kBAAkB,YAAY;AAAA,MAC5C,iBAAiB,MAAM,QAAQ,IAAI,kBAAkB,UAAU,IAC3D,IAAI,kBAAkB,WAAW,SACjC;AAAA,MACJ,gBAAgB,MAAM,QAAQ,IAAI,kBAAkB,SAAS,IACzD,IAAI,kBAAkB,UAAU,SAChC;AAAA,IACN,IACA;AACJ,QAAI,IAAI,aAAa,QAAQ;AAC3B,cAAQ,KAAK,0DAA0D;AAAA,QACrE,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,QAAQ,IAAI,MAAM,OAAO;AAAA,QACzB,eAAe,IAAI,MAAM,YAAY;AAAA,QACrC,qBAAqB,IAAI,MAAM,SAAS;AAAA,QACxC,wBAAwB,IAAI,0BAA0B;AAAA,QACtD,sBAAsB,MAAM,QAAQ,IAAI,eAAe,IAAI,IAAI,gBAAgB,SAAS;AAAA,QACxF;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,wBAAwB,KAA4B,gBAA8B;AAEhG,MAAI,IAAI,MAAM,iBAAiB,QAAQ,IAAI,mBAAmB,eAAe,MAAM;AACjF;AAAA,EACF;AAEA,QAAM,aAAa,IAAI,0BAA0B,IAAI,MAAM,SAAS;AACpE,MAAI,cAAc,eAAe,gBAAgB;AAC/C,sBAAkB,KAAK,gBAAgB,UAAU;AACjD,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,EACrD;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/commands/types.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport { randomUUID } from 'crypto'\nimport type { AuthContext } from '../auth/server'\nimport type { OrganizationScope } from '@open-mercato/core/modules/directory/utils/organizationScope'\n\nexport type CommandRuntimeContext = {\n container: AwilixContainer\n auth: AuthContext | null\n organizationScope: OrganizationScope | null\n selectedOrganizationId: string | null\n organizationIds: string[] | null\n request?: Request\n}\n\nexport type CommandLogMetadata = {\n tenantId?: string | null\n organizationId?: string | null\n actorUserId?: string | null\n actionLabel?: string | null\n resourceKind?: string | null\n resourceId?: string | null\n undoToken?: string | null\n payload?: unknown\n snapshotBefore?: unknown\n snapshotAfter?: unknown\n changes?: Record<string, unknown> | null\n context?: Record<string, unknown> | null\n}\n\nexport type CommandExecuteResult<TResult> = {\n result: TResult\n logEntry: any | null\n}\n\nexport type CommandLogBuilderArgs<TInput, TResult> = {\n input: TInput\n result: TResult\n ctx: CommandRuntimeContext\n snapshots: {\n before?: unknown\n after?: unknown\n }\n}\n\nexport interface CommandHandler<TInput = unknown, TResult = unknown> {\n readonly id: string\n readonly isUndoable?: boolean\n prepare?(input: TInput, ctx: CommandRuntimeContext): Promise<{ before?: unknown } | null> | { before?: unknown } | null\n execute(input: TInput, ctx: CommandRuntimeContext): Promise<TResult> | TResult\n buildLog?(args: CommandLogBuilderArgs<TInput, TResult>): Promise<CommandLogMetadata | null | undefined> | CommandLogMetadata | null | undefined\n captureAfter?(input: TInput, result: TResult, ctx: CommandRuntimeContext): Promise<unknown> | unknown\n undo?(params: { input: TInput; ctx: CommandRuntimeContext; logEntry: any }): Promise<void> | void\n}\n\nexport type CommandExecutionOptions<TInput> = {\n input: TInput\n ctx: CommandRuntimeContext\n metadata?: CommandLogMetadata | null\n}\n\nexport function defaultUndoToken(): string {\n return randomUUID()\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,kBAAkB;AA2DpB,SAAS,mBAA2B;AACzC,SAAO,WAAW;AACpB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { analyzeCacheSegments, purgeCacheSegment } from "../cache/segments.js";
|
|
2
|
+
const CRUD_CACHE_PATTERN = "crud|*";
|
|
3
|
+
const CRUD_CACHE_STATS_KEY = "crud-cache-stats";
|
|
4
|
+
function sanitizeSegment(value) {
|
|
5
|
+
return value.replace(/[^a-zA-Z0-9:_/|-]/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
6
|
+
}
|
|
7
|
+
function normalizePathSegment(path, resource) {
|
|
8
|
+
if (!path) return null;
|
|
9
|
+
let trimmed = path.trim();
|
|
10
|
+
if (!trimmed) return null;
|
|
11
|
+
trimmed = trimmed.replace(/^https?:\/\/[^/]+/i, "");
|
|
12
|
+
trimmed = trimmed.replace(/^\/+/, "");
|
|
13
|
+
if (trimmed.startsWith("backend/")) trimmed = trimmed.slice("backend/".length);
|
|
14
|
+
if (trimmed.startsWith("api/")) trimmed = trimmed.slice("api/".length);
|
|
15
|
+
trimmed = trimmed.replace(/^\/+/, "").replace(/\/+/g, "/");
|
|
16
|
+
trimmed = trimmed.replace(/\/$/, "");
|
|
17
|
+
if (!trimmed) return null;
|
|
18
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
19
|
+
if (!segments.length) return null;
|
|
20
|
+
const resourceSegments = resource ? resource.split(".").filter(Boolean) : [];
|
|
21
|
+
if (resourceSegments.length && segments[0] === resourceSegments[0]) {
|
|
22
|
+
segments.shift();
|
|
23
|
+
}
|
|
24
|
+
const formatted = segments.join("-");
|
|
25
|
+
return formatted.length ? sanitizeSegment(formatted) : null;
|
|
26
|
+
}
|
|
27
|
+
function parseCrudCacheKey(key) {
|
|
28
|
+
const parts = key.split("|");
|
|
29
|
+
if (parts.length < 4) {
|
|
30
|
+
return { resource: null, method: null, path: null, segment: null };
|
|
31
|
+
}
|
|
32
|
+
const resource = parts[1] ?? null;
|
|
33
|
+
const method = parts[2] ?? null;
|
|
34
|
+
const path = parts[3] ?? null;
|
|
35
|
+
const normalizedPath = normalizePathSegment(path, resource);
|
|
36
|
+
const fallback = resource ? sanitizeSegment(resource.replace(/[.]/g, "-")) : null;
|
|
37
|
+
const segment = normalizedPath ?? fallback;
|
|
38
|
+
return {
|
|
39
|
+
resource,
|
|
40
|
+
method,
|
|
41
|
+
path,
|
|
42
|
+
segment
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function deriveCrudSegmentTag(resource, request) {
|
|
46
|
+
const url = new URL(request.url);
|
|
47
|
+
const pathSegment = normalizePathSegment(url.pathname, resource);
|
|
48
|
+
const fallback = sanitizeSegment(resource.replace(/[.]/g, "-"));
|
|
49
|
+
const finalSegment = pathSegment ?? fallback;
|
|
50
|
+
return finalSegment || "crud-root";
|
|
51
|
+
}
|
|
52
|
+
async function collectCrudCacheStats(cache) {
|
|
53
|
+
const analyses = await analyzeCacheSegments(cache, {
|
|
54
|
+
keysPattern: CRUD_CACHE_PATTERN,
|
|
55
|
+
deriveSegment: (key) => parseCrudCacheKey(key).segment,
|
|
56
|
+
filterKey: (key) => key !== CRUD_CACHE_STATS_KEY
|
|
57
|
+
});
|
|
58
|
+
const segments = analyses.map(({ segment, keys }) => {
|
|
59
|
+
const sample = keys[0] ?? null;
|
|
60
|
+
const details = sample ? parseCrudCacheKey(sample) : { resource: null, method: null, path: null, segment: null };
|
|
61
|
+
return {
|
|
62
|
+
segment,
|
|
63
|
+
resource: details.resource,
|
|
64
|
+
method: details.method,
|
|
65
|
+
path: details.path,
|
|
66
|
+
keyCount: keys.length,
|
|
67
|
+
keys
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
const stats = {
|
|
71
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
72
|
+
segments,
|
|
73
|
+
totalKeys: segments.reduce((sum, entry) => sum + entry.keyCount, 0)
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
await cache.set(CRUD_CACHE_STATS_KEY, stats, { tags: ["crud-cache-stats"] });
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
return stats;
|
|
80
|
+
}
|
|
81
|
+
async function purgeCrudCacheSegment(cache, segment) {
|
|
82
|
+
return purgeCacheSegment(
|
|
83
|
+
cache,
|
|
84
|
+
{
|
|
85
|
+
keysPattern: CRUD_CACHE_PATTERN,
|
|
86
|
+
deriveSegment: (key) => parseCrudCacheKey(key).segment,
|
|
87
|
+
filterKey: (key) => key !== CRUD_CACHE_STATS_KEY
|
|
88
|
+
},
|
|
89
|
+
segment
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
export {
|
|
93
|
+
CRUD_CACHE_STATS_KEY,
|
|
94
|
+
collectCrudCacheStats,
|
|
95
|
+
deriveCrudSegmentTag,
|
|
96
|
+
purgeCrudCacheSegment
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=cache-stats.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/crud/cache-stats.ts"],
|
|
4
|
+
"sourcesContent": ["import type { CacheStrategy } from '@open-mercato/cache'\nimport { analyzeCacheSegments, purgeCacheSegment } from '../cache/segments'\n\nexport type CrudCacheSegmentInfo = {\n segment: string\n resource: string | null\n method: string | null\n path: string | null\n keyCount: number\n keys: string[]\n}\n\nexport type CrudCacheStats = {\n generatedAt: string\n segments: CrudCacheSegmentInfo[]\n totalKeys: number\n}\n\nconst CRUD_CACHE_PATTERN = 'crud|*'\nexport const CRUD_CACHE_STATS_KEY = 'crud-cache-stats'\n\nfunction sanitizeSegment(value: string): string {\n return value\n .replace(/[^a-zA-Z0-9:_/|-]/g, '-')\n .replace(/-{2,}/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n\nfunction normalizePathSegment(path: string | null | undefined, resource: string | null | undefined): string | null {\n if (!path) return null\n let trimmed = path.trim()\n if (!trimmed) return null\n trimmed = trimmed.replace(/^https?:\\/\\/[^/]+/i, '')\n trimmed = trimmed.replace(/^\\/+/, '')\n if (trimmed.startsWith('backend/')) trimmed = trimmed.slice('backend/'.length)\n if (trimmed.startsWith('api/')) trimmed = trimmed.slice('api/'.length)\n trimmed = trimmed.replace(/^\\/+/, '').replace(/\\/+/g, '/')\n trimmed = trimmed.replace(/\\/$/, '')\n if (!trimmed) return null\n const segments = trimmed.split('/').filter(Boolean)\n if (!segments.length) return null\n const resourceSegments = resource ? resource.split('.').filter(Boolean) : []\n if (resourceSegments.length && segments[0] === resourceSegments[0]) {\n segments.shift()\n }\n const formatted = segments.join('-')\n return formatted.length ? sanitizeSegment(formatted) : null\n}\n\nfunction parseCrudCacheKey(key: string): {\n resource: string | null\n method: string | null\n path: string | null\n segment: string | null\n} {\n const parts = key.split('|')\n if (parts.length < 4) {\n return { resource: null, method: null, path: null, segment: null }\n }\n const resource = parts[1] ?? null\n const method = parts[2] ?? null\n const path = parts[3] ?? null\n const normalizedPath = normalizePathSegment(path, resource)\n const fallback = resource ? sanitizeSegment(resource.replace(/[.]/g, '-')) : null\n const segment = normalizedPath ?? fallback\n return {\n resource,\n method,\n path,\n segment,\n }\n}\n\nexport function deriveCrudSegmentTag(resource: string, request: Request): string {\n const url = new URL(request.url)\n const pathSegment = normalizePathSegment(url.pathname, resource)\n const fallback = sanitizeSegment(resource.replace(/[.]/g, '-'))\n const finalSegment = pathSegment ?? fallback\n return finalSegment || 'crud-root'\n}\n\nexport async function collectCrudCacheStats(cache: CacheStrategy): Promise<CrudCacheStats> {\n const analyses = await analyzeCacheSegments(cache, {\n keysPattern: CRUD_CACHE_PATTERN,\n deriveSegment: (key) => parseCrudCacheKey(key).segment,\n filterKey: (key) => key !== CRUD_CACHE_STATS_KEY,\n })\n\n const segments: CrudCacheSegmentInfo[] = analyses.map(({ segment, keys }) => {\n const sample = keys[0] ?? null\n const details = sample ? parseCrudCacheKey(sample) : { resource: null, method: null, path: null, segment: null }\n return {\n segment,\n resource: details.resource,\n method: details.method,\n path: details.path,\n keyCount: keys.length,\n keys,\n }\n })\n\n const stats: CrudCacheStats = {\n generatedAt: new Date().toISOString(),\n segments,\n totalKeys: segments.reduce((sum, entry) => sum + entry.keyCount, 0),\n }\n\n try {\n await cache.set(CRUD_CACHE_STATS_KEY, stats, { tags: ['crud-cache-stats'] })\n } catch {\n // best effort write; ignore failure\n }\n\n return stats\n}\n\nexport async function purgeCrudCacheSegment(cache: CacheStrategy, segment: string): Promise<{ deleted: number; keys: string[] }> {\n return purgeCacheSegment(\n cache,\n {\n keysPattern: CRUD_CACHE_PATTERN,\n deriveSegment: (key) => parseCrudCacheKey(key).segment,\n filterKey: (key) => key !== CRUD_CACHE_STATS_KEY,\n },\n segment,\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { parseBooleanToken } from "../boolean.js";
|
|
2
|
+
let crudCacheEnabledFlag = null;
|
|
3
|
+
function isCrudCacheEnabled() {
|
|
4
|
+
if (crudCacheEnabledFlag !== null) return crudCacheEnabledFlag;
|
|
5
|
+
crudCacheEnabledFlag = parseBooleanToken(process.env.ENABLE_CRUD_API_CACHE ?? "") === true;
|
|
6
|
+
return crudCacheEnabledFlag;
|
|
7
|
+
}
|
|
8
|
+
let crudCacheDebugFlag = null;
|
|
9
|
+
function isCrudCacheDebugEnabled() {
|
|
10
|
+
if (crudCacheDebugFlag !== null) return crudCacheDebugFlag;
|
|
11
|
+
crudCacheDebugFlag = parseBooleanToken(process.env.QUERY_ENGINE_DEBUG_SQL ?? "") === true;
|
|
12
|
+
return crudCacheDebugFlag;
|
|
13
|
+
}
|
|
14
|
+
function debugCrudCache(event, context) {
|
|
15
|
+
if (!isCrudCacheDebugEnabled()) return;
|
|
16
|
+
try {
|
|
17
|
+
console.debug("[crud][cache]", event, context);
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function resolveCrudCache(container) {
|
|
22
|
+
try {
|
|
23
|
+
const cache = container.resolve("cache");
|
|
24
|
+
if (cache && typeof cache.get === "function" && typeof cache.set === "function") {
|
|
25
|
+
return cache;
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function normalizeTagSegment(value) {
|
|
32
|
+
if (value === null || value === void 0 || value === "") return "null";
|
|
33
|
+
return value.toString().trim().replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
34
|
+
}
|
|
35
|
+
function canonicalizeResourceTag(value) {
|
|
36
|
+
if (value === null || value === void 0) return null;
|
|
37
|
+
const trimmed = String(value).trim();
|
|
38
|
+
if (!trimmed.length) return null;
|
|
39
|
+
const withSeparators = trimmed.replace(/::/g, ".").replace(/[/\\]+/g, ".").replace(/[\s]+/g, ".").replace(/_/g, ".").replace(/-+/g, ".");
|
|
40
|
+
const withCamelBreaks = withSeparators.replace(/([a-z0-9])([A-Z])/g, "$1.$2");
|
|
41
|
+
const collapsed = withCamelBreaks.replace(/\.{2,}/g, ".").replace(/^\.+|\.+$/g, "");
|
|
42
|
+
const lowered = collapsed.toLowerCase();
|
|
43
|
+
return lowered.length ? lowered : null;
|
|
44
|
+
}
|
|
45
|
+
function buildRecordTag(resource, tenantId, recordId) {
|
|
46
|
+
return [
|
|
47
|
+
"crud",
|
|
48
|
+
normalizeTagSegment(resource),
|
|
49
|
+
"tenant",
|
|
50
|
+
normalizeTagSegment(tenantId),
|
|
51
|
+
"record",
|
|
52
|
+
normalizeTagSegment(recordId)
|
|
53
|
+
].join(":");
|
|
54
|
+
}
|
|
55
|
+
function buildCollectionTags(resource, tenantId, organizationIds) {
|
|
56
|
+
const normalizedResource = normalizeTagSegment(resource);
|
|
57
|
+
const normalizedTenant = normalizeTagSegment(tenantId);
|
|
58
|
+
const tags = /* @__PURE__ */ new Set();
|
|
59
|
+
if (!organizationIds.length) {
|
|
60
|
+
tags.add(["crud", normalizedResource, "tenant", normalizedTenant, "org", "null", "collection"].join(":"));
|
|
61
|
+
return Array.from(tags);
|
|
62
|
+
}
|
|
63
|
+
for (const orgId of organizationIds) {
|
|
64
|
+
tags.add(["crud", normalizedResource, "tenant", normalizedTenant, "org", normalizeTagSegment(orgId), "collection"].join(":"));
|
|
65
|
+
}
|
|
66
|
+
return Array.from(tags);
|
|
67
|
+
}
|
|
68
|
+
function normalizeIdentifierValue(value) {
|
|
69
|
+
if (value === null || value === void 0) return null;
|
|
70
|
+
if (typeof value === "string") return value;
|
|
71
|
+
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
72
|
+
if (typeof value === "object") {
|
|
73
|
+
if (value instanceof Date) return value.toISOString();
|
|
74
|
+
if (value && typeof value.id !== "undefined") {
|
|
75
|
+
return normalizeIdentifierValue(value.id);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return String(value);
|
|
79
|
+
}
|
|
80
|
+
function pickFirstIdentifier(...values) {
|
|
81
|
+
for (const value of values) {
|
|
82
|
+
const normalized = normalizeIdentifierValue(value);
|
|
83
|
+
if (normalized) return normalized;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
function singularizeSegment(segment) {
|
|
88
|
+
const lower = segment.toLowerCase();
|
|
89
|
+
if (lower.endsWith("ies") && lower.length > 3) return lower.slice(0, -3) + "y";
|
|
90
|
+
if (lower.endsWith("ses") && lower.length > 3) return lower.slice(0, -2);
|
|
91
|
+
if ((lower.endsWith("xes") || lower.endsWith("zes") || lower.endsWith("ches") || lower.endsWith("shes")) && lower.length > 3) {
|
|
92
|
+
return lower.slice(0, -2);
|
|
93
|
+
}
|
|
94
|
+
if (lower.endsWith("s") && !lower.endsWith("ss") && lower.length > 1) return lower.slice(0, -1);
|
|
95
|
+
return lower;
|
|
96
|
+
}
|
|
97
|
+
function singularizeResourceSegment(segment) {
|
|
98
|
+
return segment.split("-").map((part) => singularizeSegment(part)).join("-");
|
|
99
|
+
}
|
|
100
|
+
function deriveResourceFromCommandId(commandId) {
|
|
101
|
+
if (!commandId || typeof commandId !== "string") return null;
|
|
102
|
+
const parts = commandId.split(".");
|
|
103
|
+
if (parts.length < 2) return null;
|
|
104
|
+
const modulePart = parts[0];
|
|
105
|
+
const entityPart = singularizeResourceSegment(parts[1]);
|
|
106
|
+
if (!modulePart || !entityPart) return null;
|
|
107
|
+
return `${modulePart}.${entityPart}`;
|
|
108
|
+
}
|
|
109
|
+
function expandResourceAliases(resource, aliases) {
|
|
110
|
+
const set = /* @__PURE__ */ new Set();
|
|
111
|
+
const inputs = [resource, ...aliases ?? []];
|
|
112
|
+
for (const candidate of inputs) {
|
|
113
|
+
const canonical = canonicalizeResourceTag(candidate);
|
|
114
|
+
if (canonical) set.add(canonical);
|
|
115
|
+
}
|
|
116
|
+
if (!set.size) return [];
|
|
117
|
+
return Array.from(set);
|
|
118
|
+
}
|
|
119
|
+
async function invalidateCrudCache(container, resource, identifiers, fallbackTenant, reason, aliases) {
|
|
120
|
+
if (!isCrudCacheEnabled()) return;
|
|
121
|
+
const cache = resolveCrudCache(container);
|
|
122
|
+
if (!cache || typeof cache.deleteByTags !== "function") return;
|
|
123
|
+
const resources = expandResourceAliases(resource, aliases);
|
|
124
|
+
const tenantId = identifiers.tenantId ?? fallbackTenant ?? null;
|
|
125
|
+
const recordId = normalizeIdentifierValue(identifiers.id);
|
|
126
|
+
const tags = /* @__PURE__ */ new Set();
|
|
127
|
+
for (const key of resources) {
|
|
128
|
+
if (recordId) {
|
|
129
|
+
tags.add(buildRecordTag(key, tenantId, recordId));
|
|
130
|
+
}
|
|
131
|
+
const organizationIds = [];
|
|
132
|
+
if (identifiers.organizationId !== void 0) {
|
|
133
|
+
organizationIds.push(identifiers.organizationId ?? null);
|
|
134
|
+
}
|
|
135
|
+
if (!organizationIds.length) organizationIds.push(null);
|
|
136
|
+
for (const tag of buildCollectionTags(key, tenantId, organizationIds)) {
|
|
137
|
+
tags.add(tag);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (!tags.size) return;
|
|
141
|
+
const tagList = Array.from(tags);
|
|
142
|
+
debugCrudCache("invalidate", {
|
|
143
|
+
resource,
|
|
144
|
+
aliases: resources,
|
|
145
|
+
reason,
|
|
146
|
+
tenantId: tenantId ?? "null",
|
|
147
|
+
tags: tagList,
|
|
148
|
+
action: "clearing"
|
|
149
|
+
});
|
|
150
|
+
const deleted = await cache.deleteByTags(tagList);
|
|
151
|
+
debugCrudCache("invalidate", {
|
|
152
|
+
resource,
|
|
153
|
+
reason,
|
|
154
|
+
tenantId: tenantId ?? "null",
|
|
155
|
+
tags: tagList,
|
|
156
|
+
action: "cleared",
|
|
157
|
+
deleted
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
export {
|
|
161
|
+
buildCollectionTags,
|
|
162
|
+
buildRecordTag,
|
|
163
|
+
canonicalizeResourceTag,
|
|
164
|
+
debugCrudCache,
|
|
165
|
+
deriveResourceFromCommandId,
|
|
166
|
+
expandResourceAliases,
|
|
167
|
+
invalidateCrudCache,
|
|
168
|
+
isCrudCacheDebugEnabled,
|
|
169
|
+
isCrudCacheEnabled,
|
|
170
|
+
normalizeIdentifierValue,
|
|
171
|
+
normalizeTagSegment,
|
|
172
|
+
pickFirstIdentifier,
|
|
173
|
+
resolveCrudCache
|
|
174
|
+
};
|
|
175
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/crud/cache.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { parseBooleanToken } from '../boolean'\n\nexport type CrudCacheIdentifiers = {\n id?: string | null\n organizationId?: string | null\n tenantId?: string | null\n}\n\nlet crudCacheEnabledFlag: boolean | null = null\nexport function isCrudCacheEnabled(): boolean {\n if (crudCacheEnabledFlag !== null) return crudCacheEnabledFlag\n crudCacheEnabledFlag = parseBooleanToken(process.env.ENABLE_CRUD_API_CACHE ?? '') === true\n return crudCacheEnabledFlag\n}\n\nlet crudCacheDebugFlag: boolean | null = null\nexport function isCrudCacheDebugEnabled(): boolean {\n if (crudCacheDebugFlag !== null) return crudCacheDebugFlag\n crudCacheDebugFlag = parseBooleanToken(process.env.QUERY_ENGINE_DEBUG_SQL ?? '') === true\n return crudCacheDebugFlag\n}\n\nexport function debugCrudCache(event: string, context: Record<string, unknown>) {\n if (!isCrudCacheDebugEnabled()) return\n try {\n console.debug('[crud][cache]', event, context)\n } catch {}\n}\n\nexport function resolveCrudCache(container: AwilixContainer): CacheStrategy | null {\n try {\n const cache = (container.resolve('cache') as CacheStrategy)\n if (cache && typeof cache.get === 'function' && typeof cache.set === 'function') {\n return cache\n }\n } catch {}\n return null\n}\n\nexport function normalizeTagSegment(value: string | null | undefined): string {\n if (value === null || value === undefined || value === '') return 'null'\n return value.toString().trim().replace(/[^a-zA-Z0-9._-]/g, '-')\n}\n\nexport function canonicalizeResourceTag(value: string | null | undefined): string | null {\n if (value === null || value === undefined) return null\n const trimmed = String(value).trim()\n if (!trimmed.length) return null\n const withSeparators = trimmed\n .replace(/::/g, '.')\n .replace(/[/\\\\]+/g, '.')\n .replace(/[\\s]+/g, '.')\n .replace(/_/g, '.')\n .replace(/-+/g, '.')\n const withCamelBreaks = withSeparators.replace(/([a-z0-9])([A-Z])/g, '$1.$2')\n const collapsed = withCamelBreaks.replace(/\\.{2,}/g, '.').replace(/^\\.+|\\.+$/g, '')\n const lowered = collapsed.toLowerCase()\n return lowered.length ? lowered : null\n}\n\nexport function buildRecordTag(resource: string, tenantId: string | null, recordId: string): string {\n return [\n 'crud',\n normalizeTagSegment(resource),\n 'tenant',\n normalizeTagSegment(tenantId),\n 'record',\n normalizeTagSegment(recordId),\n ].join(':')\n}\n\nexport function buildCollectionTags(\n resource: string,\n tenantId: string | null,\n organizationIds: Array<string | null>\n): string[] {\n const normalizedResource = normalizeTagSegment(resource)\n const normalizedTenant = normalizeTagSegment(tenantId)\n const tags = new Set<string>()\n if (!organizationIds.length) {\n tags.add(['crud', normalizedResource, 'tenant', normalizedTenant, 'org', 'null', 'collection'].join(':'))\n return Array.from(tags)\n }\n for (const orgId of organizationIds) {\n tags.add(['crud', normalizedResource, 'tenant', normalizedTenant, 'org', normalizeTagSegment(orgId), 'collection'].join(':'))\n }\n return Array.from(tags)\n}\n\nexport function normalizeIdentifierValue(value: unknown): string | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n if (typeof value === 'object') {\n if (value instanceof Date) return value.toISOString()\n if (value && typeof (value as { id?: unknown }).id !== 'undefined') {\n return normalizeIdentifierValue((value as { id?: unknown }).id)\n }\n }\n return String(value)\n}\n\nexport function pickFirstIdentifier(...values: Array<unknown>): string | null {\n for (const value of values) {\n const normalized = normalizeIdentifierValue(value)\n if (normalized) return normalized\n }\n return null\n}\n\nfunction singularizeSegment(segment: string): string {\n const lower = segment.toLowerCase()\n if (lower.endsWith('ies') && lower.length > 3) return lower.slice(0, -3) + 'y'\n if (lower.endsWith('ses') && lower.length > 3) return lower.slice(0, -2)\n if (\n (lower.endsWith('xes') ||\n lower.endsWith('zes') ||\n lower.endsWith('ches') ||\n lower.endsWith('shes')) &&\n lower.length > 3\n ) {\n return lower.slice(0, -2)\n }\n if (lower.endsWith('s') && !lower.endsWith('ss') && lower.length > 1) return lower.slice(0, -1)\n return lower\n}\n\nfunction singularizeResourceSegment(segment: string): string {\n return segment\n .split('-')\n .map((part) => singularizeSegment(part))\n .join('-')\n}\n\nexport function deriveResourceFromCommandId(commandId: string | undefined | null): string | null {\n if (!commandId || typeof commandId !== 'string') return null\n const parts = commandId.split('.')\n if (parts.length < 2) return null\n const modulePart = parts[0]\n const entityPart = singularizeResourceSegment(parts[1])\n if (!modulePart || !entityPart) return null\n return `${modulePart}.${entityPart}`\n}\n\nexport function expandResourceAliases(resource: string, aliases?: string[]): string[] {\n const set = new Set<string>()\n const inputs = [resource, ...(aliases ?? [])]\n for (const candidate of inputs) {\n const canonical = canonicalizeResourceTag(candidate)\n if (canonical) set.add(canonical)\n }\n if (!set.size) return []\n return Array.from(set)\n}\n\nexport async function invalidateCrudCache(\n container: AwilixContainer,\n resource: string,\n identifiers: CrudCacheIdentifiers,\n fallbackTenant: string | null,\n reason: string,\n aliases?: string[]\n): Promise<void> {\n if (!isCrudCacheEnabled()) return\n const cache = resolveCrudCache(container)\n if (!cache || typeof cache.deleteByTags !== 'function') return\n const resources = expandResourceAliases(resource, aliases)\n const tenantId = identifiers.tenantId ?? fallbackTenant ?? null\n const recordId = normalizeIdentifierValue(identifiers.id)\n const tags = new Set<string>()\n for (const key of resources) {\n if (recordId) {\n tags.add(buildRecordTag(key, tenantId, recordId))\n }\n const organizationIds: Array<string | null> = []\n if (identifiers.organizationId !== undefined) {\n organizationIds.push(identifiers.organizationId ?? null)\n }\n if (!organizationIds.length) organizationIds.push(null)\n for (const tag of buildCollectionTags(key, tenantId, organizationIds)) {\n tags.add(tag)\n }\n }\n if (!tags.size) return\n const tagList = Array.from(tags)\n debugCrudCache('invalidate', {\n resource,\n aliases: resources,\n reason,\n tenantId: tenantId ?? 'null',\n tags: tagList,\n action: 'clearing',\n })\n const deleted = await cache.deleteByTags(tagList)\n debugCrudCache('invalidate', {\n resource,\n reason,\n tenantId: tenantId ?? 'null',\n tags: tagList,\n action: 'cleared',\n deleted,\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
function extractCustomFieldEntries(item) {
|
|
2
|
+
const out = {};
|
|
3
|
+
if (!item || typeof item !== "object") return out;
|
|
4
|
+
const normalizeValue = (value) => {
|
|
5
|
+
if (Array.isArray(value)) return value;
|
|
6
|
+
if (typeof value !== "string") return value;
|
|
7
|
+
const trimmed = value.trim();
|
|
8
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
9
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
10
|
+
if (!inner) return [];
|
|
11
|
+
return inner.split(/[\s,]+/).map((entry) => entry.trim()).filter(Boolean);
|
|
12
|
+
}
|
|
13
|
+
return trimmed;
|
|
14
|
+
};
|
|
15
|
+
const assign = (rawKey, rawValue) => {
|
|
16
|
+
if (typeof rawKey !== "string") return;
|
|
17
|
+
const trimmed = rawKey.trim();
|
|
18
|
+
if (!trimmed) return;
|
|
19
|
+
out[`cf_${trimmed}`] = normalizeValue(rawValue);
|
|
20
|
+
};
|
|
21
|
+
for (const [rawKey, rawValue] of Object.entries(item)) {
|
|
22
|
+
if (rawKey.startsWith("cf_")) {
|
|
23
|
+
if (rawKey.endsWith("__is_multi")) continue;
|
|
24
|
+
out[rawKey] = normalizeValue(rawValue);
|
|
25
|
+
} else if (rawKey.startsWith("cf:")) {
|
|
26
|
+
assign(rawKey.slice(3), rawValue);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const customValues = item.customValues;
|
|
30
|
+
if (customValues && typeof customValues === "object" && !Array.isArray(customValues)) {
|
|
31
|
+
for (const [key, value] of Object.entries(customValues)) {
|
|
32
|
+
assign(key, value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const customFields = item.customFields;
|
|
36
|
+
if (Array.isArray(customFields)) {
|
|
37
|
+
for (const entry of customFields) {
|
|
38
|
+
const key = entry && typeof entry.key === "string" ? entry.key : null;
|
|
39
|
+
if (!key) continue;
|
|
40
|
+
assign(key, "value" in entry ? entry.value : void 0);
|
|
41
|
+
}
|
|
42
|
+
} else if (customFields && typeof customFields === "object") {
|
|
43
|
+
for (const [key, value] of Object.entries(customFields)) {
|
|
44
|
+
assign(key, value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
extractCustomFieldEntries
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=custom-fields-client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/crud/custom-fields-client.ts"],
|
|
4
|
+
"sourcesContent": ["export function extractCustomFieldEntries(item: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n if (!item || typeof item !== 'object') return out\n\n const normalizeValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value\n if (typeof value !== 'string') return value\n const trimmed = value.trim()\n if (trimmed.startsWith('{') && trimmed.endsWith('}')) {\n const inner = trimmed.slice(1, -1).trim()\n if (!inner) return []\n return inner.split(/[\\s,]+/).map((entry) => entry.trim()).filter(Boolean)\n }\n return trimmed\n }\n\n const assign = (rawKey: unknown, rawValue: unknown) => {\n if (typeof rawKey !== 'string') return\n const trimmed = rawKey.trim()\n if (!trimmed) return\n out[`cf_${trimmed}`] = normalizeValue(rawValue)\n }\n\n for (const [rawKey, rawValue] of Object.entries(item)) {\n if (rawKey.startsWith('cf_')) {\n if (rawKey.endsWith('__is_multi')) continue\n out[rawKey] = normalizeValue(rawValue)\n } else if (rawKey.startsWith('cf:')) {\n assign(rawKey.slice(3), rawValue)\n }\n }\n\n const customValues = (item as any).customValues\n if (customValues && typeof customValues === 'object' && !Array.isArray(customValues)) {\n for (const [key, value] of Object.entries(customValues as Record<string, unknown>)) {\n assign(key, value)\n }\n }\n\n const customFields = (item as any).customFields\n if (Array.isArray(customFields)) {\n for (const entry of customFields as Array<Record<string, unknown>>) {\n const key = entry && typeof entry.key === 'string' ? entry.key : null\n if (!key) continue\n assign(key, 'value' in entry ? (entry as any).value : undefined)\n }\n } else if (customFields && typeof customFields === 'object') {\n for (const [key, value] of Object.entries(customFields as Record<string, unknown>)) {\n assign(key, value)\n }\n }\n\n return out\n}\n"],
|
|
5
|
+
"mappings": "AAAO,SAAS,0BAA0B,MAAwD;AAChG,QAAM,MAA+B,CAAC;AACtC,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAE9C,QAAM,iBAAiB,CAAC,UAA4B;AAClD,QAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,QAAI,OAAO,UAAU,SAAU,QAAO;AACtC,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAG;AACpD,YAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,EAAE,KAAK;AACxC,UAAI,CAAC,MAAO,QAAO,CAAC;AACpB,aAAO,MAAM,MAAM,QAAQ,EAAE,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,IAC1E;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,CAAC,QAAiB,aAAsB;AACrD,QAAI,OAAO,WAAW,SAAU;AAChC,UAAM,UAAU,OAAO,KAAK;AAC5B,QAAI,CAAC,QAAS;AACd,QAAI,MAAM,OAAO,EAAE,IAAI,eAAe,QAAQ;AAAA,EAChD;AAEA,aAAW,CAAC,QAAQ,QAAQ,KAAK,OAAO,QAAQ,IAAI,GAAG;AACrD,QAAI,OAAO,WAAW,KAAK,GAAG;AAC5B,UAAI,OAAO,SAAS,YAAY,EAAG;AACnC,UAAI,MAAM,IAAI,eAAe,QAAQ;AAAA,IACvC,WAAW,OAAO,WAAW,KAAK,GAAG;AACnC,aAAO,OAAO,MAAM,CAAC,GAAG,QAAQ;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,eAAgB,KAAa;AACnC,MAAI,gBAAgB,OAAO,iBAAiB,YAAY,CAAC,MAAM,QAAQ,YAAY,GAAG;AACpF,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,YAAuC,GAAG;AAClF,aAAO,KAAK,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,eAAgB,KAAa;AACnC,MAAI,MAAM,QAAQ,YAAY,GAAG;AAC/B,eAAW,SAAS,cAAgD;AAClE,YAAM,MAAM,SAAS,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM;AACjE,UAAI,CAAC,IAAK;AACV,aAAO,KAAK,WAAW,QAAS,MAAc,QAAQ,MAAS;AAAA,IACjE;AAAA,EACF,WAAW,gBAAgB,OAAO,iBAAiB,UAAU;AAC3D,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,YAAuC,GAAG;AAClF,aAAO,KAAK,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|