@open-mercato/shared 0.6.6-develop.6309.1.983aeec27a → 0.6.6-develop.6313.1.618a49a6b4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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.6-develop.
|
|
4
|
+
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.6-develop.6313.1.618a49a6b4'\nexport const appVersion = APP_VERSION\n"],
|
|
5
5
|
"mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/modules/registry.js
CHANGED
|
@@ -53,12 +53,10 @@ function matchRoutePattern(pattern, pathname) {
|
|
|
53
53
|
const key = mCatchAll[1];
|
|
54
54
|
if (i >= uSegs.length) return void 0;
|
|
55
55
|
params[key] = uSegs.slice(i);
|
|
56
|
-
|
|
57
|
-
return i === uSegs.length ? params : void 0;
|
|
56
|
+
return params;
|
|
58
57
|
} else if (mOptCatch) {
|
|
59
58
|
const key = mOptCatch[1];
|
|
60
59
|
params[key] = i < uSegs.length ? uSegs.slice(i) : [];
|
|
61
|
-
i = uSegs.length;
|
|
62
60
|
return params;
|
|
63
61
|
} else if (mDyn) {
|
|
64
62
|
if (i >= uSegs.length) return void 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/modules/registry.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ReactNode } from 'react'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi/types'\nimport type { SyncCrudEventResult } from '../lib/crud/sync-event-types'\nimport type { DashboardWidgetModule } from './dashboard/widgets'\nimport type { InjectionAnyWidgetModule, ModuleInjectionTable } from './widgets/injection'\nimport type { IntegrationBundle, IntegrationDefinition } from './integrations/types'\nimport {\n applyApiOverridesToManifests,\n applyModuleOverridesToModules,\n applyPageOverridesToManifests,\n composeApiRouteOverrides,\n composePageRouteOverrides,\n} from './overrides'\n\n// Context passed to dynamic metadata guards\nexport type RouteVisibilityContext = { path?: string; auth?: any }\n\n/**\n * Portal sidebar navigation hint. When declared on a portal page's metadata,\n * the page is auto-listed in the portal sidebar (subject to RBAC) by the\n * `/api/customer_accounts/portal/nav` endpoint.\n *\n * Absence of `nav` means the page is routable but not auto-listed (useful for\n * detail pages, create forms, etc.).\n */\nexport type PortalNavMetadata = {\n label: string\n labelKey?: string\n group?: 'main' | 'account'\n order?: number\n icon?: string\n}\n\n// Metadata you can export from page.meta.ts or directly from a server page\nexport type PageMetadata = {\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: readonly string[]\n // Optional fine-grained feature requirements\n requireFeatures?: readonly string[]\n // Portal: require customer (portal user) authentication instead of staff auth\n requireCustomerAuth?: boolean\n // Portal: require customer-specific features (checked against CustomerRbacService)\n requireCustomerFeatures?: readonly string[]\n // Portal: optional sidebar presentation hint (auto-listed by portal nav endpoint)\n nav?: PortalNavMetadata\n // Titles and grouping (aliases supported)\n title?: string\n titleKey?: string\n pageTitle?: string\n pageTitleKey?: string\n group?: string\n groupKey?: string\n pageGroup?: string\n pageGroupKey?: string\n // Ordering and visuals\n order?: number\n pageOrder?: number\n icon?: ReactNode\n navHidden?: boolean\n // Dynamic flags\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n // Optional static breadcrumb trail for header\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n // Navigation context for tiered navigation:\n // - 'main' (default): Main sidebar business operations\n // - 'admin': Collapsible \"Settings & Admin\" section at bottom of sidebar\n // - 'settings': Hidden from sidebar, only accessible via Settings hub page\n // - 'profile': Profile dropdown items\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n}\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\nexport type ApiHandler = (req: Request, ctx?: any) => Promise<Response> | Response\n\nexport type ModuleSubscriberHandler = (\n payload: any,\n ctx: any\n) => Promise<void | SyncCrudEventResult> | void | SyncCrudEventResult\n\nexport type ModuleWorkerHandler = (job: unknown, ctx: unknown) => Promise<void> | void\n\nexport type ModuleRoute = {\n pattern?: string\n path?: string\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: string[]\n // Optional fine-grained feature requirements\n requireFeatures?: string[]\n // Portal: require customer (portal user) authentication instead of staff auth\n requireCustomerAuth?: boolean\n // Portal: require customer-specific features (checked against CustomerRbacService)\n requireCustomerFeatures?: string[]\n // Portal: optional sidebar presentation hint (auto-listed by portal nav endpoint)\n nav?: PortalNavMetadata\n title?: string\n titleKey?: string\n group?: string\n groupKey?: string\n icon?: ReactNode\n order?: number\n priority?: number\n navHidden?: boolean\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n Component: (props: any) => ReactNode | Promise<ReactNode>\n}\n\nexport type ModuleApiLegacy = {\n method: HttpMethod\n path: string\n handler: ApiHandler\n metadata?: Record<string, unknown>\n docs?: OpenApiMethodDoc\n}\n\nexport type ModuleApiRouteFile = {\n path: string\n handlers: Partial<Record<HttpMethod, ApiHandler>>\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: string[]\n // Optional fine-grained feature requirements for the entire route file\n // Note: per-method feature requirements should be expressed inside metadata\n requireFeatures?: string[]\n docs?: OpenApiRouteDoc\n metadata?: Partial<Record<HttpMethod, unknown>>\n}\n\nexport type ModuleApi = ModuleApiLegacy | ModuleApiRouteFile\n\nexport type RouteMatchParams = Record<string, string | string[]>\n\nexport type FrontendRouteManifestEntry = Omit<ModuleRoute, 'Component'> & {\n moduleId: string\n load: () => Promise<ModuleRoute['Component']>\n}\n\nexport type BackendRouteManifestEntry = Omit<ModuleRoute, 'Component'> & {\n moduleId: string\n load: () => Promise<ModuleRoute['Component']>\n}\n\nexport type ApiRouteManifestEntry = {\n moduleId: string\n kind: 'route-file' | 'legacy'\n path: string\n methods: HttpMethod[]\n method?: HttpMethod\n load: () => Promise<Record<string, unknown>>\n}\n\nexport type ModuleCli = {\n command: string\n run: (argv: string[]) => Promise<void> | void\n}\n\nexport type ModuleSubscriber = {\n id: string\n event: string\n persistent?: boolean\n sync?: boolean\n priority?: number\n handler: ModuleSubscriberHandler\n}\n\nexport type ModuleWorker = {\n id: string\n queue: string\n concurrency: number\n handler: ModuleWorkerHandler\n}\n\nexport type ModuleInfo = {\n name?: string\n title?: string\n version?: string\n description?: string\n author?: string\n license?: string\n homepage?: string\n copyright?: string\n // Optional hard dependencies: module ids that must be enabled\n requires?: string[]\n // Whether this module can be ejected into the app's src/modules/ for customization\n ejectable?: boolean\n}\n\nexport type ModuleDashboardWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<DashboardWidgetModule<any>>\n}\n\nexport type ModuleInjectionWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n widgetId?: string\n loader: () => Promise<InjectionAnyWidgetModule<any, any>>\n}\n\nexport type Module = {\n id: string\n info?: ModuleInfo\n backendRoutes?: ModuleRoute[]\n frontendRoutes?: ModuleRoute[]\n apis?: ModuleApi[]\n cli?: ModuleCli[]\n translations?: Record<string, Record<string, string>>\n // Optional: per-module feature declarations discovered from acl.ts (module root)\n features?: Array<{ id: string; title: string; module: string }>\n // Auto-discovered event subscribers\n subscribers?: ModuleSubscriber[]\n // Auto-discovered queue workers\n workers?: ModuleWorker[]\n // Optional: per-module declared entity extensions and custom fields (static)\n // Extensions discovered from data/extensions.ts; Custom fields discovered from ce.ts (entities[].fields)\n entityExtensions?: import('./entities').EntityExtension[]\n customFieldSets?: import('./entities').CustomFieldSet[]\n // Optional: per-module declared custom entities (virtual/logical entities)\n // Discovered from ce.ts (module root). Each entry represents an entityId with optional label/description.\n customEntities?: Array<{ id: string; label?: string; description?: string }>\n dashboardWidgets?: ModuleDashboardWidgetEntry[]\n injectionWidgets?: ModuleInjectionWidgetEntry[]\n injectionTable?: ModuleInjectionTable\n // Optional: per-module vector search configuration (discovered from vector.ts)\n vector?: import('./vector').VectorModuleConfig\n // Optional: module-specific tenant setup configuration (from setup.ts)\n setup?: import('./setup').ModuleSetupConfig\n // Optional: default encryption maps owned by the module (from encryption.ts)\n defaultEncryptionMaps?: import('./encryption').ModuleEncryptionMap[]\n // Optional: integration marketplace declarations discovered from integration.ts\n integrations?: IntegrationDefinition[]\n bundles?: IntegrationBundle[]\n}\n\nfunction normPath(s: string) {\n return (s.startsWith('/') ? s : '/' + s).replace(/\\/+$/, '') || '/'\n}\n\n// 0 = literal (most specific), 1 = dynamic [param], 2 = catch-all [...param] or [[...param]]\nfunction segmentSpecificity(seg: string): 0 | 1 | 2 {\n if (seg.startsWith('[[...') || seg.startsWith('[...')) return 2\n if (seg.startsWith('[')) return 1\n return 0\n}\n\nfunction compareRouteSpecificity(aPattern: string, bPattern: string): number {\n const aSegs = aPattern.split('/')\n const bSegs = bPattern.split('/')\n const len = Math.max(aSegs.length, bSegs.length)\n for (let i = 0; i < len; i++) {\n const av = i < aSegs.length ? segmentSpecificity(aSegs[i]) : -1\n const bv = i < bSegs.length ? segmentSpecificity(bSegs[i]) : -1\n if (av !== bv) return av - bv\n }\n return 0\n}\n\nexport function sortRoutesBySpecificity<T extends { pattern?: string; path?: string }>(routes: T[]): T[] {\n return [...routes].sort((a, b) =>\n compareRouteSpecificity(a.pattern ?? a.path ?? '/', b.pattern ?? b.path ?? '/'),\n )\n}\n\n// Memoized per-array sorted view, so direct callers (e.g., the Next.js catch-all\n// routes that import generated `frontendRoutes`/`backendRoutes`/`apiRoutes`\n// arrays) match against a specificity-sorted view even if they never call\n// `register*RouteManifests`. Keyed by array reference; generated arrays are\n// module-level constants so this caches once per process.\nconst sortedRoutesCache = new WeakMap<object, readonly unknown[]>()\n\nfunction ensureSortedRoutes<T extends { pattern?: string; path?: string }>(routes: readonly T[]): readonly T[] {\n const cached = sortedRoutesCache.get(routes) as readonly T[] | undefined\n if (cached) return cached\n const sorted = sortRoutesBySpecificity([...routes])\n sortedRoutesCache.set(routes, sorted)\n return sorted\n}\n\nexport function matchRoutePattern(pattern: string, pathname: string): RouteMatchParams | undefined {\n const p = normPath(pattern)\n const u = normPath(pathname)\n const pSegs = p.split('/').slice(1)\n const uSegs = u.split('/').slice(1)\n const params: Record<string, string | string[]> = {}\n let i = 0\n for (let j = 0; j < pSegs.length; j++, i++) {\n const seg = pSegs[j]\n const mCatchAll = seg.match(/^\\[\\.\\.\\.(.+)\\]$/)\n const mOptCatch = seg.match(/^\\[\\[\\.\\.\\.(.+)\\]\\]$/)\n const mDyn = seg.match(/^\\[(.+)\\]$/)\n if (mCatchAll) {\n const key = mCatchAll[1]\n if (i >= uSegs.length) return undefined\n params[key] = uSegs.slice(i)\n i = uSegs.length\n return i === uSegs.length ? params : undefined\n } else if (mOptCatch) {\n const key = mOptCatch[1]\n params[key] = i < uSegs.length ? uSegs.slice(i) : []\n i = uSegs.length\n return params\n } else if (mDyn) {\n if (i >= uSegs.length) return undefined\n params[mDyn[1]] = uSegs[i]\n } else {\n if (i >= uSegs.length || uSegs[i].toLowerCase() !== seg.toLowerCase()) return undefined\n }\n }\n if (i !== uSegs.length) return undefined\n return params\n}\n\nfunction getPattern(r: ModuleRoute) {\n return r.pattern ?? r.path ?? '/'\n}\n\nexport function findFrontendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.frontendRoutes ?? []\n for (const r of routes) {\n const params = matchRoutePattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findBackendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.backendRoutes ?? []\n for (const r of routes) {\n const params = matchRoutePattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findApi(modules: Module[], method: HttpMethod, pathname: string): { handler: ApiHandler; params: Record<string, string | string[]>; requireAuth?: boolean; requireRoles?: string[]; metadata?: any } | undefined {\n for (const m of modules) {\n const apis = m.apis ?? []\n for (const a of apis) {\n if ('handlers' in a) {\n const params = matchRoutePattern(a.path, pathname)\n const handler = (a.handlers as any)[method]\n if (params && handler) return { handler, params, requireAuth: a.requireAuth, requireRoles: (a as any).requireRoles, metadata: (a as any).metadata }\n } else {\n const al = a as ModuleApiLegacy\n if (al.method !== method) continue\n const params = matchRoutePattern(al.path, pathname)\n if (params) {\n return { handler: al.handler, params, metadata: al.metadata }\n }\n }\n }\n }\n}\n\nexport function findRouteManifestMatch<T extends { pattern?: string; path?: string }>(\n routes: T[],\n pathname: string\n): { route: T; params: RouteMatchParams } | undefined {\n for (const route of ensureSortedRoutes(routes)) {\n const params = matchRoutePattern(route.pattern ?? route.path ?? '/', pathname)\n if (params) {\n return { route, params }\n }\n }\n}\n\nexport function findApiRouteManifestMatch<T extends { path: string; methods: HttpMethod[] }>(\n routes: T[],\n method: HttpMethod,\n pathname: string\n): { route: T; params: RouteMatchParams } | undefined {\n for (const route of ensureSortedRoutes(routes)) {\n if (!route.methods.includes(method)) continue\n const params = matchRoutePattern(route.path, pathname)\n if (params) {\n return { route, params }\n }\n }\n}\n\nlet _backendRouteManifests: BackendRouteManifestEntry[] | null = null\n\nexport function registerBackendRouteManifests(routes: BackendRouteManifestEntry[]) {\n const pageOverrides = composePageRouteOverrides()\n const finalRoutes = Object.keys(pageOverrides).length === 0\n ? routes\n : applyPageOverridesToManifests(routes, pageOverrides, 'backend')\n _backendRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getBackendRouteManifests(): BackendRouteManifestEntry[] {\n return _backendRouteManifests ?? []\n}\n\nlet _frontendRouteManifests: FrontendRouteManifestEntry[] | null = null\n\nexport function registerFrontendRouteManifests(routes: FrontendRouteManifestEntry[]) {\n const pageOverrides = composePageRouteOverrides()\n const finalRoutes = Object.keys(pageOverrides).length === 0\n ? routes\n : applyPageOverridesToManifests(routes, pageOverrides, 'frontend')\n _frontendRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getFrontendRouteManifests(): FrontendRouteManifestEntry[] {\n return _frontendRouteManifests ?? []\n}\n\nlet _apiRouteManifests: ApiRouteManifestEntry[] | null = null\n\nexport function registerApiRouteManifests(routes: ApiRouteManifestEntry[]) {\n // Apply any `entry.overrides.routes.api` overrides registered through the\n // unified `modules.ts` dispatcher or programmatic API before storing the\n // manifest. The composer is cheap and returns an empty object when no\n // overrides exist, so this is a no-op for apps that do not opt in.\n const routeOverrides = composeApiRouteOverrides()\n const finalRoutes = Object.keys(routeOverrides).length === 0\n ? routes\n : applyApiOverridesToManifests(routes, routeOverrides)\n _apiRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getApiRouteManifests(): ApiRouteManifestEntry[] {\n return _apiRouteManifests ?? []\n}\n\n// CLI modules registry - shared between CLI and module workers\nlet _cliModules: Module[] | null = null\n\nexport function registerCliModules(modules: Module[]) {\n if (_cliModules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] CLI modules re-registered (this may occur during HMR)')\n }\n _cliModules = applyModuleOverridesToModules(modules)\n}\n\nexport function getCliModules(): Module[] {\n // Return empty array if not registered - allows generate command to work without bootstrap\n return _cliModules ?? []\n}\n\nexport function hasCliModules(): boolean {\n return _cliModules !== null && _cliModules.length > 0\n}\n\nexport function getDefaultEncryptionMaps(modules: Module[]): import('./encryption').ModuleEncryptionMap[] {\n const byEntityId = new Map<string, { moduleId: string; map: import('./encryption').ModuleEncryptionMap }>()\n\n for (const mod of modules) {\n for (const entry of mod.defaultEncryptionMaps ?? []) {\n const previous = byEntityId.get(entry.entityId)\n if (previous) {\n throw new Error(\n `[registry] Duplicate default encryption map for \"${entry.entityId}\" declared by \"${previous.moduleId}\" and \"${mod.id}\"`\n )\n }\n byEntityId.set(entry.entityId, {\n moduleId: mod.id,\n map: {\n entityId: entry.entityId,\n fields: entry.fields.map((field) => ({\n field: field.field,\n hashField: field.hashField ?? null,\n })),\n },\n })\n }\n }\n\n return Array.from(byEntityId.values(), ({ map }) => map)\n}\n\nfunction ensureLazyHandler<T extends (...args: any[]) => any>(\n loaded: unknown,\n kind: 'subscriber' | 'worker',\n id: string\n): T {\n const handler = typeof loaded === 'function'\n ? loaded\n : loaded && typeof loaded === 'object' && 'default' in loaded\n ? (loaded as Record<string, unknown>).default\n : null\n if (typeof handler !== 'function') {\n throw new Error(`[registry] Invalid ${kind} module \"${id}\" (missing default export handler)`)\n }\n return handler as T\n}\n\nexport function createLazyModuleSubscriber(\n loadModule: () => Promise<unknown>,\n id: string\n): ModuleSubscriberHandler {\n let handlerPromise: Promise<ModuleSubscriberHandler> | null = null\n return async (payload, ctx) => {\n handlerPromise ??= loadModule().then((loaded) =>\n ensureLazyHandler<ModuleSubscriberHandler>(loaded, 'subscriber', id)\n )\n const handler = await handlerPromise\n return handler(payload, ctx)\n }\n}\n\nexport function createLazyModuleWorker(\n loadModule: () => Promise<unknown>,\n id: string\n): ModuleWorkerHandler {\n let handlerPromise: Promise<ModuleWorkerHandler> | null = null\n return async (job, ctx) => {\n handlerPromise ??= loadModule().then((loaded) =>\n ensureLazyHandler<ModuleWorkerHandler>(loaded, 'worker', id)\n )\n const handler = await handlerPromise\n return handler(job, ctx)\n }\n}\n"],
|
|
5
|
-
"mappings": "AAMA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAmPP,SAAS,SAAS,GAAW;AAC3B,UAAQ,EAAE,WAAW,GAAG,IAAI,IAAI,MAAM,GAAG,QAAQ,QAAQ,EAAE,KAAK;AAClE;AAGA,SAAS,mBAAmB,KAAwB;AAClD,MAAI,IAAI,WAAW,OAAO,KAAK,IAAI,WAAW,MAAM,EAAG,QAAO;AAC9D,MAAI,IAAI,WAAW,GAAG,EAAG,QAAO;AAChC,SAAO;AACT;AAEA,SAAS,wBAAwB,UAAkB,UAA0B;AAC3E,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,QAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,MAAM,MAAM;AAC/C,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,UAAM,KAAK,IAAI,MAAM,SAAS,mBAAmB,MAAM,CAAC,CAAC,IAAI;AAC7D,UAAM,KAAK,IAAI,MAAM,SAAS,mBAAmB,MAAM,CAAC,CAAC,IAAI;AAC7D,QAAI,OAAO,GAAI,QAAO,KAAK;AAAA,EAC7B;AACA,SAAO;AACT;AAEO,SAAS,wBAAuE,QAAkB;AACvG,SAAO,CAAC,GAAG,MAAM,EAAE;AAAA,IAAK,CAAC,GAAG,MAC1B,wBAAwB,EAAE,WAAW,EAAE,QAAQ,KAAK,EAAE,WAAW,EAAE,QAAQ,GAAG;AAAA,EAChF;AACF;AAOA,MAAM,oBAAoB,oBAAI,QAAoC;AAElE,SAAS,mBAAkE,QAAoC;AAC7G,QAAM,SAAS,kBAAkB,IAAI,MAAM;AAC3C,MAAI,OAAQ,QAAO;AACnB,QAAM,SAAS,wBAAwB,CAAC,GAAG,MAAM,CAAC;AAClD,oBAAkB,IAAI,QAAQ,MAAM;AACpC,SAAO;AACT;AAEO,SAAS,kBAAkB,SAAiB,UAAgD;AACjG,QAAM,IAAI,SAAS,OAAO;AAC1B,QAAM,IAAI,SAAS,QAAQ;AAC3B,QAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,MAAM,CAAC;AAClC,QAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,MAAM,CAAC;AAClC,QAAM,SAA4C,CAAC;AACnD,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,KAAK;AAC1C,UAAM,MAAM,MAAM,CAAC;AACnB,UAAM,YAAY,IAAI,MAAM,kBAAkB;AAC9C,UAAM,YAAY,IAAI,MAAM,sBAAsB;AAClD,UAAM,OAAO,IAAI,MAAM,YAAY;AACnC,QAAI,WAAW;AACb,YAAM,MAAM,UAAU,CAAC;AACvB,UAAI,KAAK,MAAM,OAAQ,QAAO;AAC9B,aAAO,GAAG,IAAI,MAAM,MAAM,CAAC;AAC3B,
|
|
4
|
+
"sourcesContent": ["import type { ReactNode } from 'react'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi/types'\nimport type { SyncCrudEventResult } from '../lib/crud/sync-event-types'\nimport type { DashboardWidgetModule } from './dashboard/widgets'\nimport type { InjectionAnyWidgetModule, ModuleInjectionTable } from './widgets/injection'\nimport type { IntegrationBundle, IntegrationDefinition } from './integrations/types'\nimport {\n applyApiOverridesToManifests,\n applyModuleOverridesToModules,\n applyPageOverridesToManifests,\n composeApiRouteOverrides,\n composePageRouteOverrides,\n} from './overrides'\n\n// Context passed to dynamic metadata guards\nexport type RouteVisibilityContext = { path?: string; auth?: any }\n\n/**\n * Portal sidebar navigation hint. When declared on a portal page's metadata,\n * the page is auto-listed in the portal sidebar (subject to RBAC) by the\n * `/api/customer_accounts/portal/nav` endpoint.\n *\n * Absence of `nav` means the page is routable but not auto-listed (useful for\n * detail pages, create forms, etc.).\n */\nexport type PortalNavMetadata = {\n label: string\n labelKey?: string\n group?: 'main' | 'account'\n order?: number\n icon?: string\n}\n\n// Metadata you can export from page.meta.ts or directly from a server page\nexport type PageMetadata = {\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: readonly string[]\n // Optional fine-grained feature requirements\n requireFeatures?: readonly string[]\n // Portal: require customer (portal user) authentication instead of staff auth\n requireCustomerAuth?: boolean\n // Portal: require customer-specific features (checked against CustomerRbacService)\n requireCustomerFeatures?: readonly string[]\n // Portal: optional sidebar presentation hint (auto-listed by portal nav endpoint)\n nav?: PortalNavMetadata\n // Titles and grouping (aliases supported)\n title?: string\n titleKey?: string\n pageTitle?: string\n pageTitleKey?: string\n group?: string\n groupKey?: string\n pageGroup?: string\n pageGroupKey?: string\n // Ordering and visuals\n order?: number\n pageOrder?: number\n icon?: ReactNode\n navHidden?: boolean\n // Dynamic flags\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n // Optional static breadcrumb trail for header\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n // Navigation context for tiered navigation:\n // - 'main' (default): Main sidebar business operations\n // - 'admin': Collapsible \"Settings & Admin\" section at bottom of sidebar\n // - 'settings': Hidden from sidebar, only accessible via Settings hub page\n // - 'profile': Profile dropdown items\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n}\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\nexport type ApiHandler = (req: Request, ctx?: any) => Promise<Response> | Response\n\nexport type ModuleSubscriberHandler = (\n payload: any,\n ctx: any\n) => Promise<void | SyncCrudEventResult> | void | SyncCrudEventResult\n\nexport type ModuleWorkerHandler = (job: unknown, ctx: unknown) => Promise<void> | void\n\nexport type ModuleRoute = {\n pattern?: string\n path?: string\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: string[]\n // Optional fine-grained feature requirements\n requireFeatures?: string[]\n // Portal: require customer (portal user) authentication instead of staff auth\n requireCustomerAuth?: boolean\n // Portal: require customer-specific features (checked against CustomerRbacService)\n requireCustomerFeatures?: string[]\n // Portal: optional sidebar presentation hint (auto-listed by portal nav endpoint)\n nav?: PortalNavMetadata\n title?: string\n titleKey?: string\n group?: string\n groupKey?: string\n icon?: ReactNode\n order?: number\n priority?: number\n navHidden?: boolean\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n Component: (props: any) => ReactNode | Promise<ReactNode>\n}\n\nexport type ModuleApiLegacy = {\n method: HttpMethod\n path: string\n handler: ApiHandler\n metadata?: Record<string, unknown>\n docs?: OpenApiMethodDoc\n}\n\nexport type ModuleApiRouteFile = {\n path: string\n handlers: Partial<Record<HttpMethod, ApiHandler>>\n requireAuth?: boolean\n /** @deprecated Use `requireFeatures` instead \u2014 role names are mutable and can be spoofed */\n requireRoles?: string[]\n // Optional fine-grained feature requirements for the entire route file\n // Note: per-method feature requirements should be expressed inside metadata\n requireFeatures?: string[]\n docs?: OpenApiRouteDoc\n metadata?: Partial<Record<HttpMethod, unknown>>\n}\n\nexport type ModuleApi = ModuleApiLegacy | ModuleApiRouteFile\n\nexport type RouteMatchParams = Record<string, string | string[]>\n\nexport type FrontendRouteManifestEntry = Omit<ModuleRoute, 'Component'> & {\n moduleId: string\n load: () => Promise<ModuleRoute['Component']>\n}\n\nexport type BackendRouteManifestEntry = Omit<ModuleRoute, 'Component'> & {\n moduleId: string\n load: () => Promise<ModuleRoute['Component']>\n}\n\nexport type ApiRouteManifestEntry = {\n moduleId: string\n kind: 'route-file' | 'legacy'\n path: string\n methods: HttpMethod[]\n method?: HttpMethod\n load: () => Promise<Record<string, unknown>>\n}\n\nexport type ModuleCli = {\n command: string\n run: (argv: string[]) => Promise<void> | void\n}\n\nexport type ModuleSubscriber = {\n id: string\n event: string\n persistent?: boolean\n sync?: boolean\n priority?: number\n handler: ModuleSubscriberHandler\n}\n\nexport type ModuleWorker = {\n id: string\n queue: string\n concurrency: number\n handler: ModuleWorkerHandler\n}\n\nexport type ModuleInfo = {\n name?: string\n title?: string\n version?: string\n description?: string\n author?: string\n license?: string\n homepage?: string\n copyright?: string\n // Optional hard dependencies: module ids that must be enabled\n requires?: string[]\n // Whether this module can be ejected into the app's src/modules/ for customization\n ejectable?: boolean\n}\n\nexport type ModuleDashboardWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<DashboardWidgetModule<any>>\n}\n\nexport type ModuleInjectionWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n widgetId?: string\n loader: () => Promise<InjectionAnyWidgetModule<any, any>>\n}\n\nexport type Module = {\n id: string\n info?: ModuleInfo\n backendRoutes?: ModuleRoute[]\n frontendRoutes?: ModuleRoute[]\n apis?: ModuleApi[]\n cli?: ModuleCli[]\n translations?: Record<string, Record<string, string>>\n // Optional: per-module feature declarations discovered from acl.ts (module root)\n features?: Array<{ id: string; title: string; module: string }>\n // Auto-discovered event subscribers\n subscribers?: ModuleSubscriber[]\n // Auto-discovered queue workers\n workers?: ModuleWorker[]\n // Optional: per-module declared entity extensions and custom fields (static)\n // Extensions discovered from data/extensions.ts; Custom fields discovered from ce.ts (entities[].fields)\n entityExtensions?: import('./entities').EntityExtension[]\n customFieldSets?: import('./entities').CustomFieldSet[]\n // Optional: per-module declared custom entities (virtual/logical entities)\n // Discovered from ce.ts (module root). Each entry represents an entityId with optional label/description.\n customEntities?: Array<{ id: string; label?: string; description?: string }>\n dashboardWidgets?: ModuleDashboardWidgetEntry[]\n injectionWidgets?: ModuleInjectionWidgetEntry[]\n injectionTable?: ModuleInjectionTable\n // Optional: per-module vector search configuration (discovered from vector.ts)\n vector?: import('./vector').VectorModuleConfig\n // Optional: module-specific tenant setup configuration (from setup.ts)\n setup?: import('./setup').ModuleSetupConfig\n // Optional: default encryption maps owned by the module (from encryption.ts)\n defaultEncryptionMaps?: import('./encryption').ModuleEncryptionMap[]\n // Optional: integration marketplace declarations discovered from integration.ts\n integrations?: IntegrationDefinition[]\n bundles?: IntegrationBundle[]\n}\n\nfunction normPath(s: string) {\n return (s.startsWith('/') ? s : '/' + s).replace(/\\/+$/, '') || '/'\n}\n\n// 0 = literal (most specific), 1 = dynamic [param], 2 = catch-all [...param] or [[...param]]\nfunction segmentSpecificity(seg: string): 0 | 1 | 2 {\n if (seg.startsWith('[[...') || seg.startsWith('[...')) return 2\n if (seg.startsWith('[')) return 1\n return 0\n}\n\nfunction compareRouteSpecificity(aPattern: string, bPattern: string): number {\n const aSegs = aPattern.split('/')\n const bSegs = bPattern.split('/')\n const len = Math.max(aSegs.length, bSegs.length)\n for (let i = 0; i < len; i++) {\n const av = i < aSegs.length ? segmentSpecificity(aSegs[i]) : -1\n const bv = i < bSegs.length ? segmentSpecificity(bSegs[i]) : -1\n if (av !== bv) return av - bv\n }\n return 0\n}\n\nexport function sortRoutesBySpecificity<T extends { pattern?: string; path?: string }>(routes: T[]): T[] {\n return [...routes].sort((a, b) =>\n compareRouteSpecificity(a.pattern ?? a.path ?? '/', b.pattern ?? b.path ?? '/'),\n )\n}\n\n// Memoized per-array sorted view, so direct callers (e.g., the Next.js catch-all\n// routes that import generated `frontendRoutes`/`backendRoutes`/`apiRoutes`\n// arrays) match against a specificity-sorted view even if they never call\n// `register*RouteManifests`. Keyed by array reference; generated arrays are\n// module-level constants so this caches once per process.\nconst sortedRoutesCache = new WeakMap<object, readonly unknown[]>()\n\nfunction ensureSortedRoutes<T extends { pattern?: string; path?: string }>(routes: readonly T[]): readonly T[] {\n const cached = sortedRoutesCache.get(routes) as readonly T[] | undefined\n if (cached) return cached\n const sorted = sortRoutesBySpecificity([...routes])\n sortedRoutesCache.set(routes, sorted)\n return sorted\n}\n\nexport function matchRoutePattern(pattern: string, pathname: string): RouteMatchParams | undefined {\n const p = normPath(pattern)\n const u = normPath(pathname)\n const pSegs = p.split('/').slice(1)\n const uSegs = u.split('/').slice(1)\n const params: Record<string, string | string[]> = {}\n let i = 0\n for (let j = 0; j < pSegs.length; j++, i++) {\n const seg = pSegs[j]\n const mCatchAll = seg.match(/^\\[\\.\\.\\.(.+)\\]$/)\n const mOptCatch = seg.match(/^\\[\\[\\.\\.\\.(.+)\\]\\]$/)\n const mDyn = seg.match(/^\\[(.+)\\]$/)\n if (mCatchAll) {\n const key = mCatchAll[1]\n if (i >= uSegs.length) return undefined\n params[key] = uSegs.slice(i)\n return params\n } else if (mOptCatch) {\n const key = mOptCatch[1]\n params[key] = i < uSegs.length ? uSegs.slice(i) : []\n return params\n } else if (mDyn) {\n if (i >= uSegs.length) return undefined\n params[mDyn[1]] = uSegs[i]\n } else {\n if (i >= uSegs.length || uSegs[i].toLowerCase() !== seg.toLowerCase()) return undefined\n }\n }\n if (i !== uSegs.length) return undefined\n return params\n}\n\nfunction getPattern(r: ModuleRoute) {\n return r.pattern ?? r.path ?? '/'\n}\n\nexport function findFrontendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.frontendRoutes ?? []\n for (const r of routes) {\n const params = matchRoutePattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findBackendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.backendRoutes ?? []\n for (const r of routes) {\n const params = matchRoutePattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findApi(modules: Module[], method: HttpMethod, pathname: string): { handler: ApiHandler; params: Record<string, string | string[]>; requireAuth?: boolean; requireRoles?: string[]; metadata?: any } | undefined {\n for (const m of modules) {\n const apis = m.apis ?? []\n for (const a of apis) {\n if ('handlers' in a) {\n const params = matchRoutePattern(a.path, pathname)\n const handler = (a.handlers as any)[method]\n if (params && handler) return { handler, params, requireAuth: a.requireAuth, requireRoles: (a as any).requireRoles, metadata: (a as any).metadata }\n } else {\n const al = a as ModuleApiLegacy\n if (al.method !== method) continue\n const params = matchRoutePattern(al.path, pathname)\n if (params) {\n return { handler: al.handler, params, metadata: al.metadata }\n }\n }\n }\n }\n}\n\nexport function findRouteManifestMatch<T extends { pattern?: string; path?: string }>(\n routes: T[],\n pathname: string\n): { route: T; params: RouteMatchParams } | undefined {\n for (const route of ensureSortedRoutes(routes)) {\n const params = matchRoutePattern(route.pattern ?? route.path ?? '/', pathname)\n if (params) {\n return { route, params }\n }\n }\n}\n\nexport function findApiRouteManifestMatch<T extends { path: string; methods: HttpMethod[] }>(\n routes: T[],\n method: HttpMethod,\n pathname: string\n): { route: T; params: RouteMatchParams } | undefined {\n for (const route of ensureSortedRoutes(routes)) {\n if (!route.methods.includes(method)) continue\n const params = matchRoutePattern(route.path, pathname)\n if (params) {\n return { route, params }\n }\n }\n}\n\nlet _backendRouteManifests: BackendRouteManifestEntry[] | null = null\n\nexport function registerBackendRouteManifests(routes: BackendRouteManifestEntry[]) {\n const pageOverrides = composePageRouteOverrides()\n const finalRoutes = Object.keys(pageOverrides).length === 0\n ? routes\n : applyPageOverridesToManifests(routes, pageOverrides, 'backend')\n _backendRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getBackendRouteManifests(): BackendRouteManifestEntry[] {\n return _backendRouteManifests ?? []\n}\n\nlet _frontendRouteManifests: FrontendRouteManifestEntry[] | null = null\n\nexport function registerFrontendRouteManifests(routes: FrontendRouteManifestEntry[]) {\n const pageOverrides = composePageRouteOverrides()\n const finalRoutes = Object.keys(pageOverrides).length === 0\n ? routes\n : applyPageOverridesToManifests(routes, pageOverrides, 'frontend')\n _frontendRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getFrontendRouteManifests(): FrontendRouteManifestEntry[] {\n return _frontendRouteManifests ?? []\n}\n\nlet _apiRouteManifests: ApiRouteManifestEntry[] | null = null\n\nexport function registerApiRouteManifests(routes: ApiRouteManifestEntry[]) {\n // Apply any `entry.overrides.routes.api` overrides registered through the\n // unified `modules.ts` dispatcher or programmatic API before storing the\n // manifest. The composer is cheap and returns an empty object when no\n // overrides exist, so this is a no-op for apps that do not opt in.\n const routeOverrides = composeApiRouteOverrides()\n const finalRoutes = Object.keys(routeOverrides).length === 0\n ? routes\n : applyApiOverridesToManifests(routes, routeOverrides)\n _apiRouteManifests = sortRoutesBySpecificity(finalRoutes)\n}\n\nexport function getApiRouteManifests(): ApiRouteManifestEntry[] {\n return _apiRouteManifests ?? []\n}\n\n// CLI modules registry - shared between CLI and module workers\nlet _cliModules: Module[] | null = null\n\nexport function registerCliModules(modules: Module[]) {\n if (_cliModules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] CLI modules re-registered (this may occur during HMR)')\n }\n _cliModules = applyModuleOverridesToModules(modules)\n}\n\nexport function getCliModules(): Module[] {\n // Return empty array if not registered - allows generate command to work without bootstrap\n return _cliModules ?? []\n}\n\nexport function hasCliModules(): boolean {\n return _cliModules !== null && _cliModules.length > 0\n}\n\nexport function getDefaultEncryptionMaps(modules: Module[]): import('./encryption').ModuleEncryptionMap[] {\n const byEntityId = new Map<string, { moduleId: string; map: import('./encryption').ModuleEncryptionMap }>()\n\n for (const mod of modules) {\n for (const entry of mod.defaultEncryptionMaps ?? []) {\n const previous = byEntityId.get(entry.entityId)\n if (previous) {\n throw new Error(\n `[registry] Duplicate default encryption map for \"${entry.entityId}\" declared by \"${previous.moduleId}\" and \"${mod.id}\"`\n )\n }\n byEntityId.set(entry.entityId, {\n moduleId: mod.id,\n map: {\n entityId: entry.entityId,\n fields: entry.fields.map((field) => ({\n field: field.field,\n hashField: field.hashField ?? null,\n })),\n },\n })\n }\n }\n\n return Array.from(byEntityId.values(), ({ map }) => map)\n}\n\nfunction ensureLazyHandler<T extends (...args: any[]) => any>(\n loaded: unknown,\n kind: 'subscriber' | 'worker',\n id: string\n): T {\n const handler = typeof loaded === 'function'\n ? loaded\n : loaded && typeof loaded === 'object' && 'default' in loaded\n ? (loaded as Record<string, unknown>).default\n : null\n if (typeof handler !== 'function') {\n throw new Error(`[registry] Invalid ${kind} module \"${id}\" (missing default export handler)`)\n }\n return handler as T\n}\n\nexport function createLazyModuleSubscriber(\n loadModule: () => Promise<unknown>,\n id: string\n): ModuleSubscriberHandler {\n let handlerPromise: Promise<ModuleSubscriberHandler> | null = null\n return async (payload, ctx) => {\n handlerPromise ??= loadModule().then((loaded) =>\n ensureLazyHandler<ModuleSubscriberHandler>(loaded, 'subscriber', id)\n )\n const handler = await handlerPromise\n return handler(payload, ctx)\n }\n}\n\nexport function createLazyModuleWorker(\n loadModule: () => Promise<unknown>,\n id: string\n): ModuleWorkerHandler {\n let handlerPromise: Promise<ModuleWorkerHandler> | null = null\n return async (job, ctx) => {\n handlerPromise ??= loadModule().then((loaded) =>\n ensureLazyHandler<ModuleWorkerHandler>(loaded, 'worker', id)\n )\n const handler = await handlerPromise\n return handler(job, ctx)\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/shared",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.6313.1.618a49a6b4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"@mikro-orm/core": "^7.1.4",
|
|
94
94
|
"@mikro-orm/decorators": "^7.1.4",
|
|
95
95
|
"@mikro-orm/postgresql": "^7.1.4",
|
|
96
|
-
"@open-mercato/cache": "0.6.6-develop.
|
|
96
|
+
"@open-mercato/cache": "0.6.6-develop.6313.1.618a49a6b4",
|
|
97
97
|
"dotenv": "^17.4.2",
|
|
98
98
|
"rate-limiter-flexible": "^11.2.0",
|
|
99
99
|
"re2js": "2.8.3",
|
|
@@ -251,6 +251,26 @@ describe('CLI Modules Registry', () => {
|
|
|
251
251
|
it('returns undefined when static segments do not match even case-insensitively', () => {
|
|
252
252
|
expect(matchRoutePattern('/login', '/sign-in')).toBeUndefined()
|
|
253
253
|
})
|
|
254
|
+
|
|
255
|
+
it('captures all trailing segments after a static prefix for catch-all (issue #3622)', () => {
|
|
256
|
+
expect(matchRoutePattern('/files/[...path]', '/files/a/b/c')).toEqual({ path: ['a', 'b', 'c'] })
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('captures a single trailing segment for catch-all (issue #3622)', () => {
|
|
260
|
+
expect(matchRoutePattern('/files/[...path]', '/files/a')).toEqual({ path: ['a'] })
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it('returns undefined when a catch-all has no segment to consume (issue #3622)', () => {
|
|
264
|
+
expect(matchRoutePattern('/files/[...path]', '/files')).toBeUndefined()
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('captures trailing segments for optional catch-all (issue #3622)', () => {
|
|
268
|
+
expect(matchRoutePattern('/docs/[[...slug]]', '/docs/a/b')).toEqual({ slug: ['a', 'b'] })
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('returns an empty array for optional catch-all with no trailing segments (issue #3622)', () => {
|
|
272
|
+
expect(matchRoutePattern('/docs/[[...slug]]', '/docs')).toEqual({ slug: [] })
|
|
273
|
+
})
|
|
254
274
|
})
|
|
255
275
|
|
|
256
276
|
describe('findBackendMatch', () => {
|
package/src/modules/registry.ts
CHANGED
|
@@ -313,12 +313,10 @@ export function matchRoutePattern(pattern: string, pathname: string): RouteMatch
|
|
|
313
313
|
const key = mCatchAll[1]
|
|
314
314
|
if (i >= uSegs.length) return undefined
|
|
315
315
|
params[key] = uSegs.slice(i)
|
|
316
|
-
|
|
317
|
-
return i === uSegs.length ? params : undefined
|
|
316
|
+
return params
|
|
318
317
|
} else if (mOptCatch) {
|
|
319
318
|
const key = mOptCatch[1]
|
|
320
319
|
params[key] = i < uSegs.length ? uSegs.slice(i) : []
|
|
321
|
-
i = uSegs.length
|
|
322
320
|
return params
|
|
323
321
|
} else if (mDyn) {
|
|
324
322
|
if (i >= uSegs.length) return undefined
|