@open-mercato/shared 0.6.2-develop.3467.1.2a1818709d → 0.6.2
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 +24 -0
- package/dist/lib/bootstrap/factory.js +3 -1
- package/dist/lib/bootstrap/factory.js.map +2 -2
- package/dist/lib/commands/command-interceptor-store.js +3 -1
- package/dist/lib/commands/command-interceptor-store.js.map +2 -2
- package/dist/lib/crud/enricher-registry.js +3 -1
- package/dist/lib/crud/enricher-registry.js.map +2 -2
- package/dist/lib/crud/factory.js.map +1 -1
- package/dist/lib/crud/interceptor-registry.js +3 -1
- package/dist/lib/crud/interceptor-registry.js.map +2 -2
- package/dist/lib/crud/sync-event-runner.js +2 -8
- package/dist/lib/crud/sync-event-runner.js.map +2 -2
- package/dist/lib/db/mikro.js +27 -3
- package/dist/lib/db/mikro.js.map +2 -2
- package/dist/lib/di/container.js +16 -0
- package/dist/lib/di/container.js.map +2 -2
- package/dist/lib/events/patterns.js +2 -10
- package/dist/lib/events/patterns.js.map +2 -2
- package/dist/lib/middleware/page-executor.js +2 -1
- package/dist/lib/middleware/page-executor.js.map +2 -2
- package/dist/lib/modules/registry.js +2 -1
- package/dist/lib/modules/registry.js.map +2 -2
- package/dist/lib/notifications/handler-registry.js +3 -1
- package/dist/lib/notifications/handler-registry.js.map +2 -2
- package/dist/lib/patterns/wildcard.js +42 -0
- package/dist/lib/patterns/wildcard.js.map +7 -0
- package/dist/lib/regex/linear.js +31 -0
- package/dist/lib/regex/linear.js.map +7 -0
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/modules/entities/validation.js +15 -7
- package/dist/modules/entities/validation.js.map +2 -2
- package/dist/modules/overrides.js +857 -0
- package/dist/modules/overrides.js.map +2 -2
- package/dist/modules/registry.js +17 -4
- package/dist/modules/registry.js.map +2 -2
- package/dist/modules/widgets/injection-loader.js +12 -6
- package/dist/modules/widgets/injection-loader.js.map +2 -2
- package/jest.config.cjs +3 -1
- package/package.json +8 -8
- package/src/lib/bootstrap/factory.ts +3 -1
- package/src/lib/commands/command-interceptor-store.ts +3 -1
- package/src/lib/crud/__tests__/sync-event-runner.test.ts +5 -0
- package/src/lib/crud/enricher-registry.ts +3 -1
- package/src/lib/crud/factory.ts +2 -2
- package/src/lib/crud/interceptor-registry.ts +3 -1
- package/src/lib/crud/sync-event-runner.ts +2 -11
- package/src/lib/db/mikro.ts +27 -2
- package/src/lib/di/container.ts +24 -4
- package/src/lib/events/__tests__/patterns.test.ts +5 -0
- package/src/lib/events/patterns.ts +3 -16
- package/src/lib/middleware/page-executor.ts +2 -1
- package/src/lib/modules/registry.ts +2 -1
- package/src/lib/notifications/handler-registry.ts +3 -1
- package/src/lib/patterns/__tests__/wildcard.test.ts +27 -0
- package/src/lib/patterns/wildcard.ts +59 -0
- package/src/lib/regex/linear.ts +48 -0
- package/src/modules/__tests__/contract-overrides.test.ts +332 -0
- package/src/modules/__tests__/overrides.test.ts +23 -22
- package/src/modules/__tests__/route-overrides.test.ts +441 -0
- package/src/modules/entities/__tests__/validation.test.ts +97 -2
- package/src/modules/entities/validation.ts +14 -9
- package/src/modules/overrides.ts +1297 -27
- package/src/modules/registry.ts +27 -4
- package/src/modules/widgets/__tests__/injection-loader.required-modules.test.ts +21 -0
- package/src/modules/widgets/injection-loader.ts +12 -6
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyApiInterceptorOverridesToEntries } from "../../modules/overrides.js";
|
|
1
2
|
let _interceptorEntries = null;
|
|
2
3
|
const GLOBAL_INTERCEPTOR_KEY = "__openMercatoApiInterceptors__";
|
|
3
4
|
function readGlobalInterceptors() {
|
|
@@ -16,8 +17,9 @@ function writeGlobalInterceptors(entries) {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
function registerApiInterceptors(entries) {
|
|
20
|
+
const finalEntries = applyApiInterceptorOverridesToEntries(entries);
|
|
19
21
|
const flat = [];
|
|
20
|
-
|
|
22
|
+
finalEntries.forEach((entry, moduleOrder) => {
|
|
21
23
|
entry.interceptors.forEach((interceptor, interceptorOrder) => {
|
|
22
24
|
flat.push({
|
|
23
25
|
moduleId: entry.moduleId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/crud/interceptor-registry.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ApiInterceptor, ApiInterceptorMethod, ApiInterceptorRegistryEntry } from './api-interceptor'\n\nlet _interceptorEntries: ApiInterceptorRegistryEntry[] | null = null\nconst GLOBAL_INTERCEPTOR_KEY = '__openMercatoApiInterceptors__'\n\nfunction readGlobalInterceptors(): ApiInterceptorRegistryEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INTERCEPTOR_KEY]\n return Array.isArray(value) ? (value as ApiInterceptorRegistryEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInterceptors(entries: ApiInterceptorRegistryEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INTERCEPTOR_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nexport function registerApiInterceptors(entries: Array<{ moduleId: string; interceptors: ApiInterceptor[] }>) {\n const flat: ApiInterceptorRegistryEntry[] = []\n
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { ApiInterceptor, ApiInterceptorMethod, ApiInterceptorRegistryEntry } from './api-interceptor'\nimport { applyApiInterceptorOverridesToEntries } from '../../modules/overrides'\n\nlet _interceptorEntries: ApiInterceptorRegistryEntry[] | null = null\nconst GLOBAL_INTERCEPTOR_KEY = '__openMercatoApiInterceptors__'\n\nfunction readGlobalInterceptors(): ApiInterceptorRegistryEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_INTERCEPTOR_KEY]\n return Array.isArray(value) ? (value as ApiInterceptorRegistryEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalInterceptors(entries: ApiInterceptorRegistryEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_INTERCEPTOR_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nexport function registerApiInterceptors(entries: Array<{ moduleId: string; interceptors: ApiInterceptor[] }>) {\n const finalEntries = applyApiInterceptorOverridesToEntries(entries)\n const flat: ApiInterceptorRegistryEntry[] = []\n finalEntries.forEach((entry, moduleOrder) => {\n entry.interceptors.forEach((interceptor, interceptorOrder) => {\n flat.push({\n moduleId: entry.moduleId,\n interceptor,\n moduleOrder,\n interceptorOrder,\n })\n })\n })\n _interceptorEntries = flat\n writeGlobalInterceptors(flat)\n}\n\nexport function getAllApiInterceptors(): ApiInterceptorRegistryEntry[] {\n const globalEntries = readGlobalInterceptors()\n if (globalEntries) return globalEntries\n if (!_interceptorEntries) return []\n return _interceptorEntries\n}\n\nfunction routeMatches(targetRoute: string, routePath: string): boolean {\n if (targetRoute === '*') return true\n if (targetRoute.endsWith('/*')) {\n const prefix = targetRoute.slice(0, -2)\n return routePath === prefix || routePath.startsWith(`${prefix}/`)\n }\n return targetRoute === routePath\n}\n\nconst collisionWarnings = new Set<string>()\n\nexport function getApiInterceptorsForRoute(routePath: string, method: ApiInterceptorMethod): ApiInterceptorRegistryEntry[] {\n const matching = getAllApiInterceptors().filter((entry) => {\n const methods = entry.interceptor.methods ?? []\n return methods.includes(method) && routeMatches(entry.interceptor.targetRoute, routePath)\n })\n\n const sorted = matching.sort((a, b) => {\n const byPriority = (b.interceptor.priority ?? 0) - (a.interceptor.priority ?? 0)\n if (byPriority !== 0) return byPriority\n const byModule = a.moduleOrder - b.moduleOrder\n if (byModule !== 0) return byModule\n return a.interceptorOrder - b.interceptorOrder\n })\n\n if (process.env.NODE_ENV !== 'production') {\n for (let i = 1; i < sorted.length; i++) {\n const prev = sorted[i - 1]\n const current = sorted[i]\n const prevPriority = prev.interceptor.priority ?? 0\n const currentPriority = current.interceptor.priority ?? 0\n if (prevPriority !== currentPriority) continue\n const warningKey = `${routePath}:${method}:${prev.interceptor.id}:${current.interceptor.id}:${currentPriority}`\n if (collisionWarnings.has(warningKey)) continue\n collisionWarnings.add(warningKey)\n console.warn(\n `[UMES] Interceptors \"${prev.interceptor.id}\" and \"${current.interceptor.id}\" have the same priority (${currentPriority}) for route \"${routePath}\". Execution order is based on module registration order.`\n )\n }\n }\n\n return sorted\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,6CAA6C;AAEtD,IAAI,sBAA4D;AAChE,MAAM,yBAAyB;AAE/B,SAAS,yBAA+D;AACtE,MAAI;AACF,UAAM,QAAS,WAAuC,sBAAsB;AAC5E,WAAO,MAAM,QAAQ,KAAK,IAAK,QAA0C;AAAA,EAC3E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,wBAAwB,SAAwC;AACvE,MAAI;AACF;AAAC,IAAC,WAAuC,sBAAsB,IAAI;AAAA,EACrE,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,wBAAwB,SAAsE;AAC5G,QAAM,eAAe,sCAAsC,OAAO;AAClE,QAAM,OAAsC,CAAC;AAC7C,eAAa,QAAQ,CAAC,OAAO,gBAAgB;AAC3C,UAAM,aAAa,QAAQ,CAAC,aAAa,qBAAqB;AAC5D,WAAK,KAAK;AAAA,QACR,UAAU,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACD,wBAAsB;AACtB,0BAAwB,IAAI;AAC9B;AAEO,SAAS,wBAAuD;AACrE,QAAM,gBAAgB,uBAAuB;AAC7C,MAAI,cAAe,QAAO;AAC1B,MAAI,CAAC,oBAAqB,QAAO,CAAC;AAClC,SAAO;AACT;AAEA,SAAS,aAAa,aAAqB,WAA4B;AACrE,MAAI,gBAAgB,IAAK,QAAO;AAChC,MAAI,YAAY,SAAS,IAAI,GAAG;AAC9B,UAAM,SAAS,YAAY,MAAM,GAAG,EAAE;AACtC,WAAO,cAAc,UAAU,UAAU,WAAW,GAAG,MAAM,GAAG;AAAA,EAClE;AACA,SAAO,gBAAgB;AACzB;AAEA,MAAM,oBAAoB,oBAAI,IAAY;AAEnC,SAAS,2BAA2B,WAAmB,QAA6D;AACzH,QAAM,WAAW,sBAAsB,EAAE,OAAO,CAAC,UAAU;AACzD,UAAM,UAAU,MAAM,YAAY,WAAW,CAAC;AAC9C,WAAO,QAAQ,SAAS,MAAM,KAAK,aAAa,MAAM,YAAY,aAAa,SAAS;AAAA,EAC1F,CAAC;AAED,QAAM,SAAS,SAAS,KAAK,CAAC,GAAG,MAAM;AACrC,UAAM,cAAc,EAAE,YAAY,YAAY,MAAM,EAAE,YAAY,YAAY;AAC9E,QAAI,eAAe,EAAG,QAAO;AAC7B,UAAM,WAAW,EAAE,cAAc,EAAE;AACnC,QAAI,aAAa,EAAG,QAAO;AAC3B,WAAO,EAAE,mBAAmB,EAAE;AAAA,EAChC,CAAC;AAED,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,OAAO,OAAO,IAAI,CAAC;AACzB,YAAM,UAAU,OAAO,CAAC;AACxB,YAAM,eAAe,KAAK,YAAY,YAAY;AAClD,YAAM,kBAAkB,QAAQ,YAAY,YAAY;AACxD,UAAI,iBAAiB,gBAAiB;AACtC,YAAM,aAAa,GAAG,SAAS,IAAI,MAAM,IAAI,KAAK,YAAY,EAAE,IAAI,QAAQ,YAAY,EAAE,IAAI,eAAe;AAC7G,UAAI,kBAAkB,IAAI,UAAU,EAAG;AACvC,wBAAkB,IAAI,UAAU;AAChC,cAAQ;AAAA,QACN,wBAAwB,KAAK,YAAY,EAAE,UAAU,QAAQ,YAAY,EAAE,6BAA6B,eAAe,gBAAgB,SAAS;AAAA,MAClJ;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3
|
-
}
|
|
1
|
+
import { matchWildcardPattern } from "@open-mercato/shared/lib/patterns/wildcard";
|
|
4
2
|
function matchesEventPattern(pattern, eventId) {
|
|
5
|
-
|
|
6
|
-
if (pattern === "*") return true;
|
|
7
|
-
const escaped = escapeRegex(pattern).replace(/\\\*/g, ".*");
|
|
8
|
-
const regex = new RegExp("^" + escaped + "$");
|
|
9
|
-
return regex.test(eventId);
|
|
3
|
+
return matchWildcardPattern(eventId, pattern);
|
|
10
4
|
}
|
|
11
5
|
function collectSyncSubscribers(allSyncSubscribers, eventId) {
|
|
12
6
|
return allSyncSubscribers.filter((s) => matchesEventPattern(s.metadata.event, eventId)).sort((a, b) => (a.metadata.priority ?? 50) - (b.metadata.priority ?? 50));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/crud/sync-event-runner.ts"],
|
|
4
|
-
"sourcesContent": ["import type { SyncCrudEventPayload } from './sync-event-types'\nimport type { SyncSubscriberEntry } from './sync-subscriber-store'\n\n// ---------------------------------------------------------------------------\n// Event pattern matching\n// ---------------------------------------------------------------------------\n\
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { SyncCrudEventPayload } from './sync-event-types'\nimport type { SyncSubscriberEntry } from './sync-subscriber-store'\nimport { matchWildcardPattern } from '@open-mercato/shared/lib/patterns/wildcard'\n\n// ---------------------------------------------------------------------------\n// Event pattern matching\n// ---------------------------------------------------------------------------\n\nexport function matchesEventPattern(pattern: string, eventId: string): boolean {\n return matchWildcardPattern(eventId, pattern)\n}\n\n// ---------------------------------------------------------------------------\n// Collect matching subscribers\n// ---------------------------------------------------------------------------\n\nexport function collectSyncSubscribers(\n allSyncSubscribers: SyncSubscriberEntry[],\n eventId: string,\n): SyncSubscriberEntry[] {\n return allSyncSubscribers\n .filter((s) => matchesEventPattern(s.metadata.event, eventId))\n .sort((a, b) => (a.metadata.priority ?? 50) - (b.metadata.priority ?? 50))\n}\n\n// ---------------------------------------------------------------------------\n// Run sync before-event subscribers\n// ---------------------------------------------------------------------------\n\nexport async function runSyncBeforeEvent(\n subscribers: SyncSubscriberEntry[],\n payload: SyncCrudEventPayload,\n ctx: { resolve: <T = unknown>(name: string) => T },\n): Promise<{ ok: boolean; errorBody?: Record<string, unknown>; errorStatus?: number; modifiedPayload?: Record<string, unknown> }> {\n let currentPayload = payload.payload\n\n for (const subscriber of subscribers) {\n const result = await subscriber.handler({ ...payload, payload: currentPayload }, ctx)\n\n if (result?.ok === false) {\n const body = result.body ?? { error: result.message ?? 'Operation blocked', subscriberId: subscriber.metadata.id }\n return { ok: false, errorBody: body, errorStatus: result.status ?? 422 }\n }\n\n if (result?.modifiedPayload) {\n currentPayload = { ...currentPayload, ...result.modifiedPayload }\n }\n }\n\n return { ok: true, modifiedPayload: currentPayload !== payload.payload ? currentPayload : undefined }\n}\n\n// ---------------------------------------------------------------------------\n// Run sync after-event subscribers\n// ---------------------------------------------------------------------------\n\nexport async function runSyncAfterEvent(\n subscribers: SyncSubscriberEntry[],\n payload: SyncCrudEventPayload,\n ctx: { resolve: <T = unknown>(name: string) => T },\n): Promise<void> {\n for (const subscriber of subscribers) {\n try {\n await subscriber.handler(payload, ctx)\n } catch (error) {\n console.error(`[sync-event] after-subscriber failed: ${subscriber.metadata.id}`, error)\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "AAEA,SAAS,4BAA4B;AAM9B,SAAS,oBAAoB,SAAiB,SAA0B;AAC7E,SAAO,qBAAqB,SAAS,OAAO;AAC9C;AAMO,SAAS,uBACd,oBACA,SACuB;AACvB,SAAO,mBACJ,OAAO,CAAC,MAAM,oBAAoB,EAAE,SAAS,OAAO,OAAO,CAAC,EAC5D,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,YAAY,OAAO,EAAE,SAAS,YAAY,GAAG;AAC7E;AAMA,eAAsB,mBACpB,aACA,SACA,KACgI;AAChI,MAAI,iBAAiB,QAAQ;AAE7B,aAAW,cAAc,aAAa;AACpC,UAAM,SAAS,MAAM,WAAW,QAAQ,EAAE,GAAG,SAAS,SAAS,eAAe,GAAG,GAAG;AAEpF,QAAI,QAAQ,OAAO,OAAO;AACxB,YAAM,OAAO,OAAO,QAAQ,EAAE,OAAO,OAAO,WAAW,qBAAqB,cAAc,WAAW,SAAS,GAAG;AACjH,aAAO,EAAE,IAAI,OAAO,WAAW,MAAM,aAAa,OAAO,UAAU,IAAI;AAAA,IACzE;AAEA,QAAI,QAAQ,iBAAiB;AAC3B,uBAAiB,EAAE,GAAG,gBAAgB,GAAG,OAAO,gBAAgB;AAAA,IAClE;AAAA,EACF;AAEA,SAAO,EAAE,IAAI,MAAM,iBAAiB,mBAAmB,QAAQ,UAAU,iBAAiB,OAAU;AACtG;AAMA,eAAsB,kBACpB,aACA,SACA,KACe;AACf,aAAW,cAAc,aAAa;AACpC,QAAI;AACF,YAAM,WAAW,QAAQ,SAAS,GAAG;AAAA,IACvC,SAAS,OAAO;AACd,cAAQ,MAAM,yCAAyC,WAAW,SAAS,EAAE,IAAI,KAAK;AAAA,IACxF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/db/mikro.js
CHANGED
|
@@ -44,6 +44,17 @@ async function getOrm() {
|
|
|
44
44
|
const idleInTransactionTimeoutMs = Number.isFinite(idleInTxTimeoutEnv) ? idleInTxTimeoutEnv : process.env.NODE_ENV === "production" ? void 0 : 12e4;
|
|
45
45
|
const connectionOptions = idleSessionTimeoutMs && idleSessionTimeoutMs > 0 ? `-c idle_session_timeout=${idleSessionTimeoutMs}` : void 0;
|
|
46
46
|
const sslConfig = getSslConfig();
|
|
47
|
+
if (process.env.OM_DB_POOL_DEBUG === "1" || process.env.OM_INTEGRATION_TEST === "true") {
|
|
48
|
+
console.log("[orm] pool config", {
|
|
49
|
+
poolMin,
|
|
50
|
+
poolMax,
|
|
51
|
+
poolIdleTimeout,
|
|
52
|
+
poolAcquireTimeout,
|
|
53
|
+
idleSessionTimeoutMs,
|
|
54
|
+
idleInTransactionTimeoutMs,
|
|
55
|
+
nodeEnv: process.env.NODE_ENV
|
|
56
|
+
});
|
|
57
|
+
}
|
|
47
58
|
ormInstance = await MikroORM.init({
|
|
48
59
|
driver: PostgreSqlDriver,
|
|
49
60
|
clientUrl,
|
|
@@ -56,18 +67,31 @@ async function getOrm() {
|
|
|
56
67
|
metadataProvider: ReflectMetadataProvider,
|
|
57
68
|
// MikroORM v7 pool shape (min/max/idleTimeoutMillis). Knex-era `acquireTimeoutMillis` /
|
|
58
69
|
// `destroyTimeoutMillis` were removed; acquire wait maps to pg `connectionTimeoutMillis`
|
|
59
|
-
// below under `driverOptions`.
|
|
70
|
+
// below under `driverOptions`. Mirror `connectionTimeoutMillis` here too — older Mikro
|
|
71
|
+
// versions read it from `pool`; v7 reads from `driverOptions` but accepting both
|
|
72
|
+
// costs nothing and protects us from upstream config-merge regressions.
|
|
60
73
|
pool: {
|
|
61
74
|
min: poolMin,
|
|
62
75
|
max: poolMax,
|
|
63
|
-
idleTimeoutMillis: poolIdleTimeout
|
|
76
|
+
idleTimeoutMillis: poolIdleTimeout,
|
|
77
|
+
acquireTimeoutMillis: poolAcquireTimeout
|
|
64
78
|
},
|
|
65
79
|
// Driver options are merged into pg.PoolConfig (ClientConfig + pg-pool).
|
|
66
80
|
driverOptions: {
|
|
67
81
|
connectionTimeoutMillis: poolAcquireTimeout,
|
|
68
82
|
idle_in_transaction_session_timeout: idleInTransactionTimeoutMs,
|
|
69
83
|
options: connectionOptions,
|
|
70
|
-
ssl: sslConfig
|
|
84
|
+
ssl: sslConfig,
|
|
85
|
+
onPoolCreated: (pool) => {
|
|
86
|
+
if (process.env.OM_DB_POOL_DEBUG === "1" || process.env.OM_INTEGRATION_TEST === "true") {
|
|
87
|
+
console.log("[orm] pg pool created with options", {
|
|
88
|
+
max: pool.options?.max,
|
|
89
|
+
min: pool.options?.min,
|
|
90
|
+
idleTimeoutMillis: pool.options?.idleTimeoutMillis,
|
|
91
|
+
connectionTimeoutMillis: pool.options?.connectionTimeoutMillis
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
71
95
|
}
|
|
72
96
|
});
|
|
73
97
|
return ormInstance;
|
package/dist/lib/db/mikro.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/db/mikro.ts"],
|
|
4
|
-
"sourcesContent": ["import 'dotenv/config'\nimport 'reflect-metadata'\nimport { MikroORM } from '@mikro-orm/core'\nimport { ReflectMetadataProvider } from '@mikro-orm/decorators/legacy'\nimport { PostgreSqlDriver, type EntityManager as PostgreSqlEntityManager } from '@mikro-orm/postgresql'\nimport { getSslConfig } from './ssl'\n\nexport type AppMikroORM = MikroORM<PostgreSqlDriver, PostgreSqlEntityManager<PostgreSqlDriver>>\n\nlet ormInstance: AppMikroORM | null = null\n\n// Use globalThis so standalone apps survive duplicated shared package module instances.\nconst GLOBAL_ENTITIES_KEY = '__openMercatoOrmEntities__'\n\nfunction getRegisteredEntities(): any[] | null {\n return (globalThis as Record<string, unknown>)[GLOBAL_ENTITIES_KEY] as any[] | null ?? null\n}\n\nfunction setRegisteredEntities(entities: any[]): void {\n (globalThis as Record<string, unknown>)[GLOBAL_ENTITIES_KEY] = entities\n}\n\nexport function registerOrmEntities(entities: any[]) {\n if (getRegisteredEntities() !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] ORM entities re-registered (this may occur during HMR)')\n }\n setRegisteredEntities(entities)\n}\n\nexport function getOrmEntities(): any[] {\n const entities = getRegisteredEntities()\n if (!entities) {\n throw new Error('[Bootstrap] ORM entities not registered. Call registerOrmEntities() at bootstrap.')\n }\n return entities\n}\n\nexport async function getOrm() {\n if (ormInstance) {\n return ormInstance\n }\n\n const entities = getOrmEntities()\n const clientUrl = process.env.DATABASE_URL\n if (!clientUrl) {\n throw new Error('DATABASE_URL is not set')\n }\n\n // Parse connection pool settings from environment\n const poolMin = parseInt(process.env.DB_POOL_MIN || '2')\n const poolMax = parseInt(process.env.DB_POOL_MAX || '20')\n const poolIdleTimeout = parseInt(process.env.DB_POOL_IDLE_TIMEOUT || '3000')\n const poolAcquireTimeout = parseInt(process.env.DB_POOL_ACQUIRE_TIMEOUT || '6000')\n const idleSessionTimeoutEnv = parseInt(process.env.DB_IDLE_SESSION_TIMEOUT_MS || '')\n const idleInTxTimeoutEnv = parseInt(process.env.DB_IDLE_IN_TRANSACTION_TIMEOUT_MS || '')\n const idleSessionTimeoutMs = Number.isFinite(idleSessionTimeoutEnv)\n ? idleSessionTimeoutEnv\n : process.env.NODE_ENV === 'production'\n ? undefined\n : 600_000\n const idleInTransactionTimeoutMs = Number.isFinite(idleInTxTimeoutEnv)\n ? idleInTxTimeoutEnv\n : process.env.NODE_ENV === 'production'\n ? undefined\n : 120_000\n const connectionOptions =\n idleSessionTimeoutMs && idleSessionTimeoutMs > 0\n ? `-c idle_session_timeout=${idleSessionTimeoutMs}`\n : undefined\n\n const sslConfig = getSslConfig()\n\n ormInstance = await MikroORM.init<PostgreSqlDriver, PostgreSqlEntityManager<PostgreSqlDriver>>({\n driver: PostgreSqlDriver,\n clientUrl,\n entities,\n debug: false,\n // v7 no longer defaults to ReflectMetadataProvider. Entities in this repo use\n // `@mikro-orm/decorators/legacy`, which relies on TypeScript `emitDecoratorMetadata`\n // + reflect-metadata for type inference (nullability, column types). Without this,\n // inferred types are silently wrong at runtime.\n metadataProvider: ReflectMetadataProvider,\n // MikroORM v7 pool shape (min/max/idleTimeoutMillis). Knex-era `acquireTimeoutMillis` /\n // `destroyTimeoutMillis` were removed; acquire wait maps to pg `connectionTimeoutMillis`\n // below under `driverOptions
|
|
5
|
-
"mappings": "AAAA,OAAO;AACP,OAAO;AACP,SAAS,gBAAgB;AACzB,SAAS,+BAA+B;AACxC,SAAS,wBAAuE;AAChF,SAAS,oBAAoB;AAI7B,IAAI,cAAkC;AAGtC,MAAM,sBAAsB;AAE5B,SAAS,wBAAsC;AAC7C,SAAQ,WAAuC,mBAAmB,KAAqB;AACzF;AAEA,SAAS,sBAAsB,UAAuB;AACpD,EAAC,WAAuC,mBAAmB,IAAI;AACjE;AAEO,SAAS,oBAAoB,UAAiB;AACnD,MAAI,sBAAsB,MAAM,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC9E,YAAQ,MAAM,oEAAoE;AAAA,EACpF;AACA,wBAAsB,QAAQ;AAChC;AAEO,SAAS,iBAAwB;AACtC,QAAM,WAAW,sBAAsB;AACvC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,mFAAmF;AAAA,EACrG;AACA,SAAO;AACT;AAEA,eAAsB,SAAS;AAC7B,MAAI,aAAa;AACf,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,eAAe;AAChC,QAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAGA,QAAM,UAAU,SAAS,QAAQ,IAAI,eAAe,GAAG;AACvD,QAAM,UAAU,SAAS,QAAQ,IAAI,eAAe,IAAI;AACxD,QAAM,kBAAkB,SAAS,QAAQ,IAAI,wBAAwB,MAAM;AAC3E,QAAM,qBAAqB,SAAS,QAAQ,IAAI,2BAA2B,MAAM;AACjF,QAAM,wBAAwB,SAAS,QAAQ,IAAI,8BAA8B,EAAE;AACnF,QAAM,qBAAqB,SAAS,QAAQ,IAAI,qCAAqC,EAAE;AACvF,QAAM,uBAAuB,OAAO,SAAS,qBAAqB,IAC9D,wBACA,QAAQ,IAAI,aAAa,eACvB,SACA;AACN,QAAM,6BAA6B,OAAO,SAAS,kBAAkB,IACjE,qBACA,QAAQ,IAAI,aAAa,eACvB,SACA;AACN,QAAM,oBACJ,wBAAwB,uBAAuB,IAC3C,2BAA2B,oBAAoB,KAC/C;AAEN,QAAM,YAAY,aAAa;AAE/B,gBAAc,MAAM,SAAS,KAAkE;AAAA,IAC7F,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKP,kBAAkB;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import 'dotenv/config'\nimport 'reflect-metadata'\nimport { MikroORM } from '@mikro-orm/core'\nimport { ReflectMetadataProvider } from '@mikro-orm/decorators/legacy'\nimport { PostgreSqlDriver, type EntityManager as PostgreSqlEntityManager } from '@mikro-orm/postgresql'\nimport { getSslConfig } from './ssl'\n\nexport type AppMikroORM = MikroORM<PostgreSqlDriver, PostgreSqlEntityManager<PostgreSqlDriver>>\n\nlet ormInstance: AppMikroORM | null = null\n\n// Use globalThis so standalone apps survive duplicated shared package module instances.\nconst GLOBAL_ENTITIES_KEY = '__openMercatoOrmEntities__'\n\nfunction getRegisteredEntities(): any[] | null {\n return (globalThis as Record<string, unknown>)[GLOBAL_ENTITIES_KEY] as any[] | null ?? null\n}\n\nfunction setRegisteredEntities(entities: any[]): void {\n (globalThis as Record<string, unknown>)[GLOBAL_ENTITIES_KEY] = entities\n}\n\nexport function registerOrmEntities(entities: any[]) {\n if (getRegisteredEntities() !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] ORM entities re-registered (this may occur during HMR)')\n }\n setRegisteredEntities(entities)\n}\n\nexport function getOrmEntities(): any[] {\n const entities = getRegisteredEntities()\n if (!entities) {\n throw new Error('[Bootstrap] ORM entities not registered. Call registerOrmEntities() at bootstrap.')\n }\n return entities\n}\n\nexport async function getOrm() {\n if (ormInstance) {\n return ormInstance\n }\n\n const entities = getOrmEntities()\n const clientUrl = process.env.DATABASE_URL\n if (!clientUrl) {\n throw new Error('DATABASE_URL is not set')\n }\n\n // Parse connection pool settings from environment\n const poolMin = parseInt(process.env.DB_POOL_MIN || '2')\n const poolMax = parseInt(process.env.DB_POOL_MAX || '20')\n const poolIdleTimeout = parseInt(process.env.DB_POOL_IDLE_TIMEOUT || '3000')\n const poolAcquireTimeout = parseInt(process.env.DB_POOL_ACQUIRE_TIMEOUT || '6000')\n const idleSessionTimeoutEnv = parseInt(process.env.DB_IDLE_SESSION_TIMEOUT_MS || '')\n const idleInTxTimeoutEnv = parseInt(process.env.DB_IDLE_IN_TRANSACTION_TIMEOUT_MS || '')\n const idleSessionTimeoutMs = Number.isFinite(idleSessionTimeoutEnv)\n ? idleSessionTimeoutEnv\n : process.env.NODE_ENV === 'production'\n ? undefined\n : 600_000\n const idleInTransactionTimeoutMs = Number.isFinite(idleInTxTimeoutEnv)\n ? idleInTxTimeoutEnv\n : process.env.NODE_ENV === 'production'\n ? undefined\n : 120_000\n const connectionOptions =\n idleSessionTimeoutMs && idleSessionTimeoutMs > 0\n ? `-c idle_session_timeout=${idleSessionTimeoutMs}`\n : undefined\n\n const sslConfig = getSslConfig()\n\n if (process.env.OM_DB_POOL_DEBUG === '1' || process.env.OM_INTEGRATION_TEST === 'true') {\n console.log('[orm] pool config', {\n poolMin,\n poolMax,\n poolIdleTimeout,\n poolAcquireTimeout,\n idleSessionTimeoutMs,\n idleInTransactionTimeoutMs,\n nodeEnv: process.env.NODE_ENV,\n })\n }\n\n ormInstance = await MikroORM.init<PostgreSqlDriver, PostgreSqlEntityManager<PostgreSqlDriver>>({\n driver: PostgreSqlDriver,\n clientUrl,\n entities,\n debug: false,\n // v7 no longer defaults to ReflectMetadataProvider. Entities in this repo use\n // `@mikro-orm/decorators/legacy`, which relies on TypeScript `emitDecoratorMetadata`\n // + reflect-metadata for type inference (nullability, column types). Without this,\n // inferred types are silently wrong at runtime.\n metadataProvider: ReflectMetadataProvider,\n // MikroORM v7 pool shape (min/max/idleTimeoutMillis). Knex-era `acquireTimeoutMillis` /\n // `destroyTimeoutMillis` were removed; acquire wait maps to pg `connectionTimeoutMillis`\n // below under `driverOptions`. Mirror `connectionTimeoutMillis` here too \u2014 older Mikro\n // versions read it from `pool`; v7 reads from `driverOptions` but accepting both\n // costs nothing and protects us from upstream config-merge regressions.\n pool: {\n min: poolMin,\n max: poolMax,\n idleTimeoutMillis: poolIdleTimeout,\n acquireTimeoutMillis: poolAcquireTimeout,\n } as any,\n // Driver options are merged into pg.PoolConfig (ClientConfig + pg-pool).\n driverOptions: {\n connectionTimeoutMillis: poolAcquireTimeout,\n idle_in_transaction_session_timeout: idleInTransactionTimeoutMs,\n options: connectionOptions,\n ssl: sslConfig,\n onPoolCreated: (pool: any) => {\n if (process.env.OM_DB_POOL_DEBUG === '1' || process.env.OM_INTEGRATION_TEST === 'true') {\n console.log('[orm] pg pool created with options', {\n max: pool.options?.max,\n min: pool.options?.min,\n idleTimeoutMillis: pool.options?.idleTimeoutMillis,\n connectionTimeoutMillis: pool.options?.connectionTimeoutMillis,\n })\n }\n },\n },\n })\n\n return ormInstance\n}\n\n\nasync function closeOrmIfLoaded(): Promise<void> {\n if (ormInstance) {\n await ormInstance.close(true)\n ormInstance = null\n }\n}\n\n// In dev mode, handle reloads cleanly without leaving dangling connections.\nif (process.env.NODE_ENV !== 'production') {\n void closeOrmIfLoaded()\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/di/container.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getOrm } from "@open-mercato/shared/lib/db/mikro";
|
|
|
4
4
|
import { BasicQueryEngine } from "@open-mercato/shared/lib/query/engine";
|
|
5
5
|
import { DefaultDataEngine } from "@open-mercato/shared/lib/data/engine";
|
|
6
6
|
import { commandRegistry, CommandBus } from "@open-mercato/shared/lib/commands";
|
|
7
|
+
import { applyDiOverridesToContainer } from "@open-mercato/shared/modules/overrides";
|
|
7
8
|
const GLOBAL_KEY = "__openMercatoDiRegistrars__";
|
|
8
9
|
function getGlobalRegistrars() {
|
|
9
10
|
return globalThis[GLOBAL_KEY] ?? null;
|
|
@@ -25,6 +26,17 @@ function getDiRegistrars() {
|
|
|
25
26
|
}
|
|
26
27
|
return registrars;
|
|
27
28
|
}
|
|
29
|
+
function isAwilixResolver(value) {
|
|
30
|
+
return Boolean(value && typeof value === "object" && typeof value.resolve === "function");
|
|
31
|
+
}
|
|
32
|
+
function toAwilixRegistrations(registrations) {
|
|
33
|
+
return Object.fromEntries(
|
|
34
|
+
Object.entries(registrations).map(([key, value]) => [
|
|
35
|
+
key,
|
|
36
|
+
isAwilixResolver(value) ? value : asValue(value)
|
|
37
|
+
])
|
|
38
|
+
);
|
|
39
|
+
}
|
|
28
40
|
async function createRequestContainer() {
|
|
29
41
|
const diRegistrars = getDiRegistrars();
|
|
30
42
|
const orm = await getOrm();
|
|
@@ -71,6 +83,10 @@ async function createRequestContainer() {
|
|
|
71
83
|
}
|
|
72
84
|
} catch {
|
|
73
85
|
}
|
|
86
|
+
applyDiOverridesToContainer({
|
|
87
|
+
register: (registrations) => container.register(toAwilixRegistrations(registrations)),
|
|
88
|
+
unregister: (key) => container.register({ [key]: asValue(void 0) })
|
|
89
|
+
});
|
|
74
90
|
try {
|
|
75
91
|
const emForEnc = container.resolve("em");
|
|
76
92
|
const tenantEncryptionService = container.hasRegistration("tenantEncryptionService") ? container.resolve("tenantEncryptionService") : null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/di/container.ts"],
|
|
4
|
-
"sourcesContent": ["import { createContainer, asValue, AwilixContainer, InjectionMode } from 'awilix'\nimport { RequestContext } from '@mikro-orm/core'\nimport { getOrm } from '@open-mercato/shared/lib/db/mikro'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport { BasicQueryEngine } from '@open-mercato/shared/lib/query/engine'\nimport { DefaultDataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { commandRegistry, CommandBus } from '@open-mercato/shared/lib/commands'\n\nexport type AppContainer = AwilixContainer
|
|
5
|
-
"mappings": "AAAA,SAAS,iBAAiB,SAA0B,
|
|
4
|
+
"sourcesContent": ["import { createContainer, asValue, AwilixContainer, InjectionMode, type Resolver } from 'awilix'\nimport { RequestContext } from '@mikro-orm/core'\nimport { getOrm } from '@open-mercato/shared/lib/db/mikro'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport { BasicQueryEngine } from '@open-mercato/shared/lib/query/engine'\nimport { DefaultDataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { commandRegistry, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { applyDiOverridesToContainer } from '@open-mercato/shared/modules/overrides'\n\ntype DynamicCradle = Record<string, any>\n\nexport type AppContainer = AwilixContainer<DynamicCradle>\nexport type DiRegistrar = (container: AppContainer) => void\n\n// Registration pattern for publishable packages\n// Use globalThis to survive tsx/esbuild module duplication issue where the same\n// file can be loaded as multiple module instances when mixing dynamic and static imports\nconst GLOBAL_KEY = '__openMercatoDiRegistrars__'\n\nfunction getGlobalRegistrars(): DiRegistrar[] | null {\n return (globalThis as any)[GLOBAL_KEY] ?? null\n}\n\nfunction setGlobalRegistrars(registrars: DiRegistrar[]): void {\n (globalThis as any)[GLOBAL_KEY] = registrars\n}\n\nexport function registerDiRegistrars(registrars: DiRegistrar[]) {\n const existing = getGlobalRegistrars()\n if (existing !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] DI registrars re-registered (this may occur during HMR)')\n }\n setGlobalRegistrars(registrars)\n}\n\nexport function getDiRegistrars(): DiRegistrar[] {\n const registrars = getGlobalRegistrars()\n if (!registrars) {\n throw new Error('[Bootstrap] DI registrars not registered. Call registerDiRegistrars() at bootstrap.')\n }\n return registrars\n}\n\nfunction isAwilixResolver(value: unknown): value is Resolver<unknown> {\n return Boolean(value && typeof value === 'object' && typeof (value as { resolve?: unknown }).resolve === 'function')\n}\n\nfunction toAwilixRegistrations(registrations: Record<string, unknown>): Record<string, Resolver<any>> {\n return Object.fromEntries(\n Object.entries(registrations).map(([key, value]) => [\n key,\n isAwilixResolver(value) ? value : asValue(value),\n ]),\n )\n}\n\nexport async function createRequestContainer(): Promise<AppContainer> {\n const diRegistrars = getDiRegistrars()\n const orm = await getOrm()\n // Use a fresh event manager so request-level subscribers (e.g., encryption) don't pile up globally\n const baseEm = (RequestContext.getEntityManager() as any) ?? orm.em\n const em = baseEm.fork({ clear: true, freshEventManager: true, useContext: true }) as unknown as EntityManager\n const container = createContainer<DynamicCradle>({ injectionMode: InjectionMode.CLASSIC })\n // Core registrations\n container.register({\n em: asValue(em),\n queryEngine: asValue(new BasicQueryEngine(em, undefined, () => {\n try { return container.resolve('tenantEncryptionService') as any } catch { return null }\n })),\n dataEngine: asValue(new DefaultDataEngine(em, container as any)),\n commandRegistry: asValue(commandRegistry),\n commandBus: asValue(new CommandBus()),\n })\n // Allow modules to override/extend\n for (const reg of diRegistrars) {\n try { reg?.(container) } catch {}\n }\n // Core bootstrap (cache, event bus, encryption subscriber/KMS, module subscribers)\n try {\n const { bootstrap } = await import('@open-mercato/core/bootstrap') as any\n if (bootstrap && typeof bootstrap === 'function') {\n // Avoid double bootstrap if caller already wired it\n const alreadyBootstrapped = !!container.registrations?.eventBus\n if (!alreadyBootstrapped) {\n await bootstrap(container)\n }\n }\n } catch { /* optional */ }\n // App-level DI override (last chance)\n // This import path resolves only in the app context, not in packages\n try {\n // @ts-ignore - @/di only exists in app context, not in packages\n const appDi = await import('@/di') as any\n if (appDi?.register) {\n try {\n const maybe = appDi.register(container)\n if (maybe && typeof maybe.then === 'function') await maybe\n } catch {}\n }\n } catch {}\n applyDiOverridesToContainer({\n register: (registrations) => container.register(toAwilixRegistrations(registrations)),\n unregister: (key) => container.register({ [key]: asValue(undefined) }),\n })\n // Ensure tenant encryption subscriber is always registered on the fresh request-scoped EM\n try {\n const emForEnc = container.resolve('em') as any\n const tenantEncryptionService = container.hasRegistration('tenantEncryptionService')\n ? (container.resolve('tenantEncryptionService') as any)\n : null\n if (emForEnc && tenantEncryptionService?.isEnabled?.()) {\n const { registerTenantEncryptionSubscriber } = await import('@open-mercato/shared/lib/encryption/subscriber')\n registerTenantEncryptionSubscriber(emForEnc, tenantEncryptionService)\n }\n } catch {\n // best-effort; do not block container creation\n }\n return container\n}\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n require('server-only')\n} catch {\n // allow CLI/generator usage where Next server-only is not present\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
function getSingleSegmentPatternRegex(pattern) {
|
|
3
|
-
const existing = singleSegmentPatternCache.get(pattern);
|
|
4
|
-
if (existing) return existing;
|
|
5
|
-
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^.]+");
|
|
6
|
-
const compiled = new RegExp(`^${regexPattern}$`);
|
|
7
|
-
singleSegmentPatternCache.set(pattern, compiled);
|
|
8
|
-
return compiled;
|
|
9
|
-
}
|
|
1
|
+
import { matchWildcardPattern } from "@open-mercato/shared/lib/patterns/wildcard";
|
|
10
2
|
function matchEventPattern(eventName, pattern, options) {
|
|
11
3
|
const mode = options?.mode ?? "single-segment";
|
|
12
4
|
if (pattern === "*") return true;
|
|
@@ -23,7 +15,7 @@ function matchEventPattern(eventName, pattern, options) {
|
|
|
23
15
|
}
|
|
24
16
|
return false;
|
|
25
17
|
}
|
|
26
|
-
return
|
|
18
|
+
return matchWildcardPattern(eventName, pattern, { singleSegmentWildcard: true });
|
|
27
19
|
}
|
|
28
20
|
function matchAnyEventPattern(eventName, patterns, options) {
|
|
29
21
|
for (const pattern of patterns) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/events/patterns.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { matchWildcardPattern } from '@open-mercato/shared/lib/patterns/wildcard'\n\nexport type EventPatternMode = 'single-segment' | 'prefix'\n\ntype MatchEventPatternOptions = {\n mode?: EventPatternMode\n}\n\nexport function matchEventPattern(\n eventName: string,\n pattern: string,\n options?: MatchEventPatternOptions,\n): boolean {\n const mode = options?.mode ?? 'single-segment'\n\n if (pattern === '*') return true\n if (pattern === eventName) return true\n if (!pattern.includes('*')) return false\n\n if (mode === 'prefix') {\n if (pattern.endsWith('.*')) {\n const prefix = pattern.slice(0, -2)\n return eventName.startsWith(prefix + '.')\n }\n\n if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1)\n return eventName.startsWith(prefix)\n }\n\n return false\n }\n\n return matchWildcardPattern(eventName, pattern, { singleSegmentWildcard: true })\n}\n\nexport function matchAnyEventPattern(\n eventName: string,\n patterns: Iterable<string>,\n options?: MatchEventPatternOptions,\n): boolean {\n for (const pattern of patterns) {\n if (matchEventPattern(eventName, pattern, options)) {\n return true\n }\n }\n\n return false\n}\n\nexport function matchWebhookEventPattern(eventName: string, pattern: string): boolean {\n return matchEventPattern(eventName, pattern, { mode: 'prefix' })\n}\n\nexport function matchAnyWebhookEventPattern(eventName: string, patterns: Iterable<string>): boolean {\n return matchAnyEventPattern(eventName, patterns, { mode: 'prefix' })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,4BAA4B;AAQ9B,SAAS,kBACd,WACA,SACA,SACS;AACT,QAAM,OAAO,SAAS,QAAQ;AAE9B,MAAI,YAAY,IAAK,QAAO;AAC5B,MAAI,YAAY,UAAW,QAAO;AAClC,MAAI,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAEnC,MAAI,SAAS,UAAU;AACrB,QAAI,QAAQ,SAAS,IAAI,GAAG;AAC1B,YAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AAClC,aAAO,UAAU,WAAW,SAAS,GAAG;AAAA,IAC1C;AAEA,QAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,YAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AAClC,aAAO,UAAU,WAAW,MAAM;AAAA,IACpC;AAEA,WAAO;AAAA,EACT;AAEA,SAAO,qBAAqB,WAAW,SAAS,EAAE,uBAAuB,KAAK,CAAC;AACjF;AAEO,SAAS,qBACd,WACA,UACA,SACS;AACT,aAAW,WAAW,UAAU;AAC9B,QAAI,kBAAkB,WAAW,SAAS,OAAO,GAAG;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,yBAAyB,WAAmB,SAA0B;AACpF,SAAO,kBAAkB,WAAW,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE;AAEO,SAAS,4BAA4B,WAAmB,UAAqC;AAClG,SAAO,qBAAqB,WAAW,UAAU,EAAE,MAAM,SAAS,CAAC;AACrE;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from "@open-mercato/shared/modules/middleware/page";
|
|
2
|
+
import { applyPageGuardOverridesToEntries } from "@open-mercato/shared/modules/overrides";
|
|
2
3
|
const DEFAULT_PRIORITY = 100;
|
|
3
4
|
function shouldRunMiddleware(middleware, mode, pathname) {
|
|
4
5
|
if (middleware.mode && middleware.mode !== mode) return false;
|
|
@@ -10,7 +11,7 @@ function compareMiddleware(a, b) {
|
|
|
10
11
|
return a.id.localeCompare(b.id);
|
|
11
12
|
}
|
|
12
13
|
function flattenAndSortMiddleware(entries, mode, pathname) {
|
|
13
|
-
return entries.flatMap((entry) => entry.middleware).filter((middleware) => shouldRunMiddleware(middleware, mode, pathname)).sort(compareMiddleware);
|
|
14
|
+
return applyPageGuardOverridesToEntries(entries).flatMap((entry) => entry.middleware).filter((middleware) => shouldRunMiddleware(middleware, mode, pathname)).sort(compareMiddleware);
|
|
14
15
|
}
|
|
15
16
|
async function executePageMiddleware(args) {
|
|
16
17
|
const { entries, context, onError } = args;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/middleware/page-executor.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n PageMiddlewareContext,\n PageMiddlewareMode,\n PageMiddlewareRegistryEntry,\n PageMiddlewareResult,\n PageRouteMiddleware,\n} from '@open-mercato/shared/modules/middleware/page'\nimport { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from '@open-mercato/shared/modules/middleware/page'\n\ntype ExecutePageMiddlewareArgs = {\n entries: PageMiddlewareRegistryEntry[]\n context: PageMiddlewareContext\n onError?: (error: unknown, middleware: Pick<PageRouteMiddleware, 'id' | 'priority'>) => void\n}\n\nconst DEFAULT_PRIORITY = 100\n\nfunction shouldRunMiddleware(middleware: PageRouteMiddleware, mode: PageMiddlewareMode, pathname: string): boolean {\n if (middleware.mode && middleware.mode !== mode) return false\n return matchPageMiddlewareTarget(pathname, middleware.target)\n}\n\nfunction compareMiddleware(a: PageRouteMiddleware, b: PageRouteMiddleware): number {\n const priorityDiff = (a.priority ?? DEFAULT_PRIORITY) - (b.priority ?? DEFAULT_PRIORITY)\n if (priorityDiff !== 0) return priorityDiff\n return a.id.localeCompare(b.id)\n}\n\nfunction flattenAndSortMiddleware(\n entries: PageMiddlewareRegistryEntry[],\n mode: PageMiddlewareMode,\n pathname: string\n): PageRouteMiddleware[] {\n return entries\n .flatMap((entry) => entry.middleware)\n .filter((middleware) => shouldRunMiddleware(middleware, mode, pathname))\n .sort(compareMiddleware)\n}\n\nexport async function executePageMiddleware(args: ExecutePageMiddlewareArgs): Promise<PageMiddlewareResult> {\n const { entries, context, onError } = args\n const matchedMiddleware = flattenAndSortMiddleware(entries, context.mode, context.pathname)\n for (const middleware of matchedMiddleware) {\n try {\n const result = await middleware.run(context)\n if (result.action === 'redirect') return result\n } catch (error) {\n if (onError) {\n onError(error, { id: middleware.id, priority: middleware.priority })\n } else {\n console.error('[middleware:page] execution failed', { id: middleware.id, error })\n }\n throw error\n }\n }\n return CONTINUE_PAGE_MIDDLEWARE\n}\n\nexport async function resolvePageMiddlewareRedirect(args: ExecutePageMiddlewareArgs): Promise<string | null> {\n const result = await executePageMiddleware(args)\n if (result.action !== 'redirect') return null\n return result.location\n}\n"],
|
|
5
|
-
"mappings": "AAOA,SAAS,0BAA0B,iCAAiC;
|
|
4
|
+
"sourcesContent": ["import type {\n PageMiddlewareContext,\n PageMiddlewareMode,\n PageMiddlewareRegistryEntry,\n PageMiddlewareResult,\n PageRouteMiddleware,\n} from '@open-mercato/shared/modules/middleware/page'\nimport { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from '@open-mercato/shared/modules/middleware/page'\nimport { applyPageGuardOverridesToEntries } from '@open-mercato/shared/modules/overrides'\n\ntype ExecutePageMiddlewareArgs = {\n entries: PageMiddlewareRegistryEntry[]\n context: PageMiddlewareContext\n onError?: (error: unknown, middleware: Pick<PageRouteMiddleware, 'id' | 'priority'>) => void\n}\n\nconst DEFAULT_PRIORITY = 100\n\nfunction shouldRunMiddleware(middleware: PageRouteMiddleware, mode: PageMiddlewareMode, pathname: string): boolean {\n if (middleware.mode && middleware.mode !== mode) return false\n return matchPageMiddlewareTarget(pathname, middleware.target)\n}\n\nfunction compareMiddleware(a: PageRouteMiddleware, b: PageRouteMiddleware): number {\n const priorityDiff = (a.priority ?? DEFAULT_PRIORITY) - (b.priority ?? DEFAULT_PRIORITY)\n if (priorityDiff !== 0) return priorityDiff\n return a.id.localeCompare(b.id)\n}\n\nfunction flattenAndSortMiddleware(\n entries: PageMiddlewareRegistryEntry[],\n mode: PageMiddlewareMode,\n pathname: string\n): PageRouteMiddleware[] {\n return applyPageGuardOverridesToEntries(entries)\n .flatMap((entry) => entry.middleware)\n .filter((middleware) => shouldRunMiddleware(middleware, mode, pathname))\n .sort(compareMiddleware)\n}\n\nexport async function executePageMiddleware(args: ExecutePageMiddlewareArgs): Promise<PageMiddlewareResult> {\n const { entries, context, onError } = args\n const matchedMiddleware = flattenAndSortMiddleware(entries, context.mode, context.pathname)\n for (const middleware of matchedMiddleware) {\n try {\n const result = await middleware.run(context)\n if (result.action === 'redirect') return result\n } catch (error) {\n if (onError) {\n onError(error, { id: middleware.id, priority: middleware.priority })\n } else {\n console.error('[middleware:page] execution failed', { id: middleware.id, error })\n }\n throw error\n }\n }\n return CONTINUE_PAGE_MIDDLEWARE\n}\n\nexport async function resolvePageMiddlewareRedirect(args: ExecutePageMiddlewareArgs): Promise<string | null> {\n const result = await executePageMiddleware(args)\n if (result.action !== 'redirect') return null\n return result.location\n}\n"],
|
|
5
|
+
"mappings": "AAOA,SAAS,0BAA0B,iCAAiC;AACpE,SAAS,wCAAwC;AAQjD,MAAM,mBAAmB;AAEzB,SAAS,oBAAoB,YAAiC,MAA0B,UAA2B;AACjH,MAAI,WAAW,QAAQ,WAAW,SAAS,KAAM,QAAO;AACxD,SAAO,0BAA0B,UAAU,WAAW,MAAM;AAC9D;AAEA,SAAS,kBAAkB,GAAwB,GAAgC;AACjF,QAAM,gBAAgB,EAAE,YAAY,qBAAqB,EAAE,YAAY;AACvE,MAAI,iBAAiB,EAAG,QAAO;AAC/B,SAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAChC;AAEA,SAAS,yBACP,SACA,MACA,UACuB;AACvB,SAAO,iCAAiC,OAAO,EAC5C,QAAQ,CAAC,UAAU,MAAM,UAAU,EACnC,OAAO,CAAC,eAAe,oBAAoB,YAAY,MAAM,QAAQ,CAAC,EACtE,KAAK,iBAAiB;AAC3B;AAEA,eAAsB,sBAAsB,MAAgE;AAC1G,QAAM,EAAE,SAAS,SAAS,QAAQ,IAAI;AACtC,QAAM,oBAAoB,yBAAyB,SAAS,QAAQ,MAAM,QAAQ,QAAQ;AAC1F,aAAW,cAAc,mBAAmB;AAC1C,QAAI;AACF,YAAM,SAAS,MAAM,WAAW,IAAI,OAAO;AAC3C,UAAI,OAAO,WAAW,WAAY,QAAO;AAAA,IAC3C,SAAS,OAAO;AACd,UAAI,SAAS;AACX,gBAAQ,OAAO,EAAE,IAAI,WAAW,IAAI,UAAU,WAAW,SAAS,CAAC;AAAA,MACrE,OAAO;AACL,gBAAQ,MAAM,sCAAsC,EAAE,IAAI,WAAW,IAAI,MAAM,CAAC;AAAA,MAClF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,8BAA8B,MAAyD;AAC3G,QAAM,SAAS,MAAM,sBAAsB,IAAI;AAC/C,MAAI,OAAO,WAAW,WAAY,QAAO;AACzC,SAAO,OAAO;AAChB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { applyModuleOverridesToModules } from "@open-mercato/shared/modules/overrides";
|
|
1
2
|
let _modules = null;
|
|
2
3
|
function registerModules(modules) {
|
|
3
4
|
if (_modules !== null && process.env.NODE_ENV === "development") {
|
|
4
5
|
console.debug("[Bootstrap] Modules re-registered (this may occur during HMR)");
|
|
5
6
|
}
|
|
6
|
-
_modules = modules;
|
|
7
|
+
_modules = applyModuleOverridesToModules(modules);
|
|
7
8
|
}
|
|
8
9
|
function getModules() {
|
|
9
10
|
if (!_modules) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/modules/registry.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Module } from '@open-mercato/shared/modules/registry'\n\n// Registration pattern for publishable packages\nlet _modules: Module[] | null = null\n\nexport function registerModules(modules: Module[]) {\n if (_modules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')\n }\n _modules = modules\n}\n\nexport function getModules(): Module[] {\n if (!_modules) {\n throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')\n }\n return _modules\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { Module } from '@open-mercato/shared/modules/registry'\nimport { applyModuleOverridesToModules } from '@open-mercato/shared/modules/overrides'\n\n// Registration pattern for publishable packages\nlet _modules: Module[] | null = null\n\nexport function registerModules(modules: Module[]) {\n if (_modules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')\n }\n _modules = applyModuleOverridesToModules(modules)\n}\n\nexport function getModules(): Module[] {\n if (!_modules) {\n throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')\n }\n return _modules\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,qCAAqC;AAG9C,IAAI,WAA4B;AAEzB,SAAS,gBAAgB,SAAmB;AACjD,MAAI,aAAa,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC/D,YAAQ,MAAM,+DAA+D;AAAA,EAC/E;AACA,aAAW,8BAA8B,OAAO;AAClD;AAEO,SAAS,aAAuB;AACrC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AACA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyNotificationHandlerOverridesToEntries } from "@open-mercato/shared/modules/overrides";
|
|
1
2
|
const GLOBAL_NOTIFICATION_HANDLERS_KEY = "__openMercatoNotificationHandlers__";
|
|
2
3
|
let _handlerEntries = null;
|
|
3
4
|
function readGlobalEntries() {
|
|
@@ -16,8 +17,9 @@ function writeGlobalEntries(entries) {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
function registerNotificationHandlers(entries) {
|
|
20
|
+
const finalEntries = applyNotificationHandlerOverridesToEntries(entries);
|
|
19
21
|
const flat = [];
|
|
20
|
-
for (const entry of
|
|
22
|
+
for (const entry of finalEntries) {
|
|
21
23
|
for (const handler of entry.handlers ?? []) {
|
|
22
24
|
flat.push({ moduleId: entry.moduleId, handler });
|
|
23
25
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/notifications/handler-registry.ts"],
|
|
4
|
-
"sourcesContent": ["import type { NotificationHandler } from '@open-mercato/shared/modules/notifications/handler'\n\nconst GLOBAL_NOTIFICATION_HANDLERS_KEY = '__openMercatoNotificationHandlers__'\n\ntype NotificationHandlerRegistryEntry = {\n moduleId: string\n handler: NotificationHandler\n}\n\nlet _handlerEntries: NotificationHandlerRegistryEntry[] | null = null\n\nfunction readGlobalEntries(): NotificationHandlerRegistryEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_NOTIFICATION_HANDLERS_KEY]\n return Array.isArray(value) ? (value as NotificationHandlerRegistryEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalEntries(entries: NotificationHandlerRegistryEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_NOTIFICATION_HANDLERS_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nexport function registerNotificationHandlers(\n entries: Array<{ moduleId: string; handlers: NotificationHandler[] }>,\n) {\n const flat: NotificationHandlerRegistryEntry[] = []\n for (const entry of
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { NotificationHandler } from '@open-mercato/shared/modules/notifications/handler'\nimport { applyNotificationHandlerOverridesToEntries } from '@open-mercato/shared/modules/overrides'\n\nconst GLOBAL_NOTIFICATION_HANDLERS_KEY = '__openMercatoNotificationHandlers__'\n\ntype NotificationHandlerRegistryEntry = {\n moduleId: string\n handler: NotificationHandler\n}\n\nlet _handlerEntries: NotificationHandlerRegistryEntry[] | null = null\n\nfunction readGlobalEntries(): NotificationHandlerRegistryEntry[] | null {\n try {\n const value = (globalThis as Record<string, unknown>)[GLOBAL_NOTIFICATION_HANDLERS_KEY]\n return Array.isArray(value) ? (value as NotificationHandlerRegistryEntry[]) : null\n } catch {\n return null\n }\n}\n\nfunction writeGlobalEntries(entries: NotificationHandlerRegistryEntry[]) {\n try {\n ;(globalThis as Record<string, unknown>)[GLOBAL_NOTIFICATION_HANDLERS_KEY] = entries\n } catch {\n // ignore global assignment failures\n }\n}\n\nexport function registerNotificationHandlers(\n entries: Array<{ moduleId: string; handlers: NotificationHandler[] }>,\n) {\n const finalEntries = applyNotificationHandlerOverridesToEntries(entries)\n const flat: NotificationHandlerRegistryEntry[] = []\n for (const entry of finalEntries) {\n for (const handler of entry.handlers ?? []) {\n flat.push({ moduleId: entry.moduleId, handler })\n }\n }\n flat.sort((a, b) => (b.handler.priority ?? 50) - (a.handler.priority ?? 50))\n _handlerEntries = flat\n writeGlobalEntries(flat)\n}\n\nexport function getNotificationHandlerEntries(): NotificationHandlerRegistryEntry[] {\n const globalEntries = readGlobalEntries()\n if (globalEntries) return globalEntries\n if (!_handlerEntries) return []\n return _handlerEntries\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,kDAAkD;AAE3D,MAAM,mCAAmC;AAOzC,IAAI,kBAA6D;AAEjE,SAAS,oBAA+D;AACtE,MAAI;AACF,UAAM,QAAS,WAAuC,gCAAgC;AACtF,WAAO,MAAM,QAAQ,KAAK,IAAK,QAA+C;AAAA,EAChF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,SAA6C;AACvE,MAAI;AACF;AAAC,IAAC,WAAuC,gCAAgC,IAAI;AAAA,EAC/E,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,6BACd,SACA;AACA,QAAM,eAAe,2CAA2C,OAAO;AACvE,QAAM,OAA2C,CAAC;AAClD,aAAW,SAAS,cAAc;AAChC,eAAW,WAAW,MAAM,YAAY,CAAC,GAAG;AAC1C,WAAK,KAAK,EAAE,UAAU,MAAM,UAAU,QAAQ,CAAC;AAAA,IACjD;AAAA,EACF;AACA,OAAK,KAAK,CAAC,GAAG,OAAO,EAAE,QAAQ,YAAY,OAAO,EAAE,QAAQ,YAAY,GAAG;AAC3E,oBAAkB;AAClB,qBAAmB,IAAI;AACzB;AAEO,SAAS,gCAAoE;AAClF,QAAM,gBAAgB,kBAAkB;AACxC,MAAI,cAAe,QAAO;AAC1B,MAAI,CAAC,gBAAiB,QAAO,CAAC;AAC9B,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function matchWildcardSegment(pattern, value, options) {
|
|
2
|
+
let patternIndex = 0;
|
|
3
|
+
let valueIndex = 0;
|
|
4
|
+
let starIndex = -1;
|
|
5
|
+
let valueRetryIndex = 0;
|
|
6
|
+
while (valueIndex < value.length) {
|
|
7
|
+
const patternChar = pattern[patternIndex];
|
|
8
|
+
const valueChar = value[valueIndex];
|
|
9
|
+
if (patternChar === "*") {
|
|
10
|
+
starIndex = patternIndex;
|
|
11
|
+
valueRetryIndex = valueIndex;
|
|
12
|
+
patternIndex += 1;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (patternChar === valueChar) {
|
|
16
|
+
patternIndex += 1;
|
|
17
|
+
valueIndex += 1;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (starIndex !== -1 && (!options.singleSegmentWildcard || value[valueRetryIndex] !== ".")) {
|
|
21
|
+
patternIndex = starIndex + 1;
|
|
22
|
+
valueRetryIndex += 1;
|
|
23
|
+
valueIndex = valueRetryIndex;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
while (pattern[patternIndex] === "*") {
|
|
29
|
+
patternIndex += 1;
|
|
30
|
+
}
|
|
31
|
+
return patternIndex === pattern.length;
|
|
32
|
+
}
|
|
33
|
+
function matchWildcardPattern(value, pattern, options = {}) {
|
|
34
|
+
if (pattern === value) return true;
|
|
35
|
+
if (pattern === "*") return true;
|
|
36
|
+
if (!pattern.includes("*")) return false;
|
|
37
|
+
return matchWildcardSegment(pattern, value, options);
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
matchWildcardPattern
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=wildcard.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/patterns/wildcard.ts"],
|
|
4
|
+
"sourcesContent": ["export type WildcardMatchOptions = {\n singleSegmentWildcard?: boolean\n}\n\nfunction matchWildcardSegment(pattern: string, value: string, options: WildcardMatchOptions): boolean {\n let patternIndex = 0\n let valueIndex = 0\n let starIndex = -1\n let valueRetryIndex = 0\n\n while (valueIndex < value.length) {\n const patternChar = pattern[patternIndex]\n const valueChar = value[valueIndex]\n\n if (patternChar === '*') {\n starIndex = patternIndex\n valueRetryIndex = valueIndex\n patternIndex += 1\n continue\n }\n\n if (patternChar === valueChar) {\n patternIndex += 1\n valueIndex += 1\n continue\n }\n\n if (\n starIndex !== -1\n && (!options.singleSegmentWildcard || value[valueRetryIndex] !== '.')\n ) {\n patternIndex = starIndex + 1\n valueRetryIndex += 1\n valueIndex = valueRetryIndex\n continue\n }\n\n return false\n }\n\n while (pattern[patternIndex] === '*') {\n patternIndex += 1\n }\n\n return patternIndex === pattern.length\n}\n\nexport function matchWildcardPattern(\n value: string,\n pattern: string,\n options: WildcardMatchOptions = {},\n): boolean {\n if (pattern === value) return true\n if (pattern === '*') return true\n if (!pattern.includes('*')) return false\n\n return matchWildcardSegment(pattern, value, options)\n}\n\n"],
|
|
5
|
+
"mappings": "AAIA,SAAS,qBAAqB,SAAiB,OAAe,SAAwC;AACpG,MAAI,eAAe;AACnB,MAAI,aAAa;AACjB,MAAI,YAAY;AAChB,MAAI,kBAAkB;AAEtB,SAAO,aAAa,MAAM,QAAQ;AAChC,UAAM,cAAc,QAAQ,YAAY;AACxC,UAAM,YAAY,MAAM,UAAU;AAElC,QAAI,gBAAgB,KAAK;AACvB,kBAAY;AACZ,wBAAkB;AAClB,sBAAgB;AAChB;AAAA,IACF;AAEA,QAAI,gBAAgB,WAAW;AAC7B,sBAAgB;AAChB,oBAAc;AACd;AAAA,IACF;AAEA,QACE,cAAc,OACV,CAAC,QAAQ,yBAAyB,MAAM,eAAe,MAAM,MACjE;AACA,qBAAe,YAAY;AAC3B,yBAAmB;AACnB,mBAAa;AACb;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,YAAY,MAAM,KAAK;AACpC,oBAAgB;AAAA,EAClB;AAEA,SAAO,iBAAiB,QAAQ;AAClC;AAEO,SAAS,qBACd,OACA,SACA,UAAgC,CAAC,GACxB;AACT,MAAI,YAAY,MAAO,QAAO;AAC9B,MAAI,YAAY,IAAK,QAAO;AAC5B,MAAI,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAEnC,SAAO,qBAAqB,SAAS,OAAO,OAAO;AACrD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { RE2JS } from "re2js";
|
|
2
|
+
const DEFAULT_MAX_PATTERN_LENGTH = 500;
|
|
3
|
+
const DEFAULT_MAX_INPUT_LENGTH = 1e4;
|
|
4
|
+
const MAX_REGEX_CACHE_SIZE = 256;
|
|
5
|
+
const compiledRegexCache = /* @__PURE__ */ new Map();
|
|
6
|
+
function getCachedRegex(pattern) {
|
|
7
|
+
const cached = compiledRegexCache.get(pattern);
|
|
8
|
+
if (cached) return cached;
|
|
9
|
+
const compiled = RE2JS.compile(pattern);
|
|
10
|
+
if (compiledRegexCache.size >= MAX_REGEX_CACHE_SIZE) {
|
|
11
|
+
const oldestKey = compiledRegexCache.keys().next().value;
|
|
12
|
+
if (oldestKey) compiledRegexCache.delete(oldestKey);
|
|
13
|
+
}
|
|
14
|
+
compiledRegexCache.set(pattern, compiled);
|
|
15
|
+
return compiled;
|
|
16
|
+
}
|
|
17
|
+
function testLinearRegex(pattern, input, options = {}) {
|
|
18
|
+
const maxPatternLength = options.maxPatternLength ?? DEFAULT_MAX_PATTERN_LENGTH;
|
|
19
|
+
const maxInputLength = options.maxInputLength ?? DEFAULT_MAX_INPUT_LENGTH;
|
|
20
|
+
if (pattern.length > maxPatternLength) return { ok: false, reason: "pattern_too_long" };
|
|
21
|
+
if (input.length > maxInputLength) return { ok: false, reason: "input_too_long" };
|
|
22
|
+
try {
|
|
23
|
+
return { ok: true, matched: getCachedRegex(pattern).test(input) };
|
|
24
|
+
} catch {
|
|
25
|
+
return { ok: false, reason: "invalid_pattern" };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
testLinearRegex
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=linear.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/regex/linear.ts"],
|
|
4
|
+
"sourcesContent": ["import { RE2JS } from 're2js'\n\nconst DEFAULT_MAX_PATTERN_LENGTH = 500\nconst DEFAULT_MAX_INPUT_LENGTH = 10_000\nconst MAX_REGEX_CACHE_SIZE = 256\n\nconst compiledRegexCache = new Map<string, RE2JS>()\n\nexport type LinearRegexTestOptions = {\n maxPatternLength?: number\n maxInputLength?: number\n}\n\nexport type LinearRegexTestResult =\n | { ok: true; matched: boolean }\n | { ok: false; reason: 'pattern_too_long' | 'input_too_long' | 'invalid_pattern' }\n\nfunction getCachedRegex(pattern: string): RE2JS {\n const cached = compiledRegexCache.get(pattern)\n if (cached) return cached\n\n const compiled = RE2JS.compile(pattern)\n if (compiledRegexCache.size >= MAX_REGEX_CACHE_SIZE) {\n const oldestKey = compiledRegexCache.keys().next().value\n if (oldestKey) compiledRegexCache.delete(oldestKey)\n }\n compiledRegexCache.set(pattern, compiled)\n return compiled\n}\n\nexport function testLinearRegex(\n pattern: string,\n input: string,\n options: LinearRegexTestOptions = {},\n): LinearRegexTestResult {\n const maxPatternLength = options.maxPatternLength ?? DEFAULT_MAX_PATTERN_LENGTH\n const maxInputLength = options.maxInputLength ?? DEFAULT_MAX_INPUT_LENGTH\n\n if (pattern.length > maxPatternLength) return { ok: false, reason: 'pattern_too_long' }\n if (input.length > maxInputLength) return { ok: false, reason: 'input_too_long' }\n\n try {\n return { ok: true, matched: getCachedRegex(pattern).test(input) }\n } catch {\n return { ok: false, reason: 'invalid_pattern' }\n }\n}\n\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,aAAa;AAEtB,MAAM,6BAA6B;AACnC,MAAM,2BAA2B;AACjC,MAAM,uBAAuB;AAE7B,MAAM,qBAAqB,oBAAI,IAAmB;AAWlD,SAAS,eAAe,SAAwB;AAC9C,QAAM,SAAS,mBAAmB,IAAI,OAAO;AAC7C,MAAI,OAAQ,QAAO;AAEnB,QAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,MAAI,mBAAmB,QAAQ,sBAAsB;AACnD,UAAM,YAAY,mBAAmB,KAAK,EAAE,KAAK,EAAE;AACnD,QAAI,UAAW,oBAAmB,OAAO,SAAS;AAAA,EACpD;AACA,qBAAmB,IAAI,SAAS,QAAQ;AACxC,SAAO;AACT;AAEO,SAAS,gBACd,SACA,OACA,UAAkC,CAAC,GACZ;AACvB,QAAM,mBAAmB,QAAQ,oBAAoB;AACrD,QAAM,iBAAiB,QAAQ,kBAAkB;AAEjD,MAAI,QAAQ,SAAS,iBAAkB,QAAO,EAAE,IAAI,OAAO,QAAQ,mBAAmB;AACtF,MAAI,MAAM,SAAS,eAAgB,QAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAEhF,MAAI;AACF,WAAO,EAAE,IAAI,MAAM,SAAS,eAAe,OAAO,EAAE,KAAK,KAAK,EAAE;AAAA,EAClE,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,QAAQ,kBAAkB;AAAA,EAChD;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/lib/version.js
CHANGED
package/dist/lib/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/version.ts"],
|
|
4
|
-
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.2
|
|
4
|
+
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.2'\nexport const appVersion = APP_VERSION\n"],
|
|
5
5
|
"mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { testLinearRegex } from "../../lib/regex/linear.js";
|
|
3
|
+
const MAX_CUSTOM_FIELD_REGEX_PATTERN_LENGTH = 500;
|
|
4
|
+
const MAX_CUSTOM_FIELD_REGEX_INPUT_LENGTH = 1e4;
|
|
2
5
|
const VALIDATION_RULES = [
|
|
3
6
|
"required",
|
|
4
7
|
"date",
|
|
@@ -23,7 +26,11 @@ const validationRuleSchema = z.discriminatedUnion("rule", [
|
|
|
23
26
|
z.object({ rule: z.literal("gte"), param: z.number(), message: z.string().min(1) }),
|
|
24
27
|
z.object({ rule: z.literal("eq"), param: z.any(), message: z.string().min(1) }),
|
|
25
28
|
z.object({ rule: z.literal("ne"), param: z.any(), message: z.string().min(1) }),
|
|
26
|
-
z.object({
|
|
29
|
+
z.object({
|
|
30
|
+
rule: z.literal("regex"),
|
|
31
|
+
param: z.string().min(1),
|
|
32
|
+
message: z.string().min(1)
|
|
33
|
+
})
|
|
27
34
|
]);
|
|
28
35
|
const validationRulesArraySchema = z.array(validationRuleSchema).max(32);
|
|
29
36
|
function evalRule(rule, value, kind) {
|
|
@@ -66,12 +73,11 @@ function evalRule(rule, value, kind) {
|
|
|
66
73
|
return value !== rule.param ? null : rule.message;
|
|
67
74
|
case "regex":
|
|
68
75
|
if (isEmpty(value)) return null;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
}
|
|
76
|
+
const regexResult = testLinearRegex(String(rule.param), String(value), {
|
|
77
|
+
maxPatternLength: MAX_CUSTOM_FIELD_REGEX_PATTERN_LENGTH,
|
|
78
|
+
maxInputLength: MAX_CUSTOM_FIELD_REGEX_INPUT_LENGTH
|
|
79
|
+
});
|
|
80
|
+
return regexResult.ok && regexResult.matched ? null : rule.message;
|
|
75
81
|
default:
|
|
76
82
|
return null;
|
|
77
83
|
}
|
|
@@ -94,6 +100,8 @@ function validateValuesAgainstDefs(values, defs) {
|
|
|
94
100
|
return { ok: Object.keys(errors).length === 0, fieldErrors: errors };
|
|
95
101
|
}
|
|
96
102
|
export {
|
|
103
|
+
MAX_CUSTOM_FIELD_REGEX_INPUT_LENGTH,
|
|
104
|
+
MAX_CUSTOM_FIELD_REGEX_PATTERN_LENGTH,
|
|
97
105
|
VALIDATION_RULES,
|
|
98
106
|
validateValuesAgainstDefs,
|
|
99
107
|
validationRuleSchema,
|