@open-mercato/shared 0.4.2-canary-c02407ff85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.mjs +101 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/api/crud.js +47 -0
- package/dist/lib/api/crud.js.map +7 -0
- package/dist/lib/api/scoped.js +140 -0
- package/dist/lib/api/scoped.js.map +7 -0
- package/dist/lib/auth/jwt.js +34 -0
- package/dist/lib/auth/jwt.js.map +7 -0
- package/dist/lib/auth/server.js +157 -0
- package/dist/lib/auth/server.js.map +7 -0
- package/dist/lib/boolean.js +22 -0
- package/dist/lib/boolean.js.map +7 -0
- package/dist/lib/bootstrap/appResolver.js +43 -0
- package/dist/lib/bootstrap/appResolver.js.map +7 -0
- package/dist/lib/bootstrap/dynamicLoader.js +108 -0
- package/dist/lib/bootstrap/dynamicLoader.js.map +7 -0
- package/dist/lib/bootstrap/factory.js +59 -0
- package/dist/lib/bootstrap/factory.js.map +7 -0
- package/dist/lib/bootstrap/index.js +11 -0
- package/dist/lib/bootstrap/index.js.map +7 -0
- package/dist/lib/bootstrap/types.js +1 -0
- package/dist/lib/bootstrap/types.js.map +7 -0
- package/dist/lib/cache/segments.js +36 -0
- package/dist/lib/cache/segments.js.map +7 -0
- package/dist/lib/cli/progress.js +46 -0
- package/dist/lib/cli/progress.js.map +7 -0
- package/dist/lib/commands/command-bus.js +285 -0
- package/dist/lib/commands/command-bus.js.map +7 -0
- package/dist/lib/commands/customFieldSnapshots.js +66 -0
- package/dist/lib/commands/customFieldSnapshots.js.map +7 -0
- package/dist/lib/commands/helpers.js +98 -0
- package/dist/lib/commands/helpers.js.map +7 -0
- package/dist/lib/commands/index.js +8 -0
- package/dist/lib/commands/index.js.map +7 -0
- package/dist/lib/commands/operationMetadata.js +32 -0
- package/dist/lib/commands/operationMetadata.js.map +7 -0
- package/dist/lib/commands/registry.js +43 -0
- package/dist/lib/commands/registry.js.map +7 -0
- package/dist/lib/commands/scope.js +44 -0
- package/dist/lib/commands/scope.js.map +7 -0
- package/dist/lib/commands/types.js +8 -0
- package/dist/lib/commands/types.js.map +7 -0
- package/dist/lib/crud/cache-stats.js +98 -0
- package/dist/lib/crud/cache-stats.js.map +7 -0
- package/dist/lib/crud/cache.js +175 -0
- package/dist/lib/crud/cache.js.map +7 -0
- package/dist/lib/crud/custom-fields-client.js +52 -0
- package/dist/lib/crud/custom-fields-client.js.map +7 -0
- package/dist/lib/crud/custom-fields.js +467 -0
- package/dist/lib/crud/custom-fields.js.map +7 -0
- package/dist/lib/crud/errors.js +24 -0
- package/dist/lib/crud/errors.js.map +7 -0
- package/dist/lib/crud/exporters.js +154 -0
- package/dist/lib/crud/exporters.js.map +7 -0
- package/dist/lib/crud/factory.js +1311 -0
- package/dist/lib/crud/factory.js.map +7 -0
- package/dist/lib/crud/types.js +1 -0
- package/dist/lib/crud/types.js.map +7 -0
- package/dist/lib/custom-fields/normalize.js +36 -0
- package/dist/lib/custom-fields/normalize.js.map +7 -0
- package/dist/lib/data/engine.js +396 -0
- package/dist/lib/data/engine.js.map +7 -0
- package/dist/lib/db/escapeLikePattern.js +5 -0
- package/dist/lib/db/escapeLikePattern.js.map +7 -0
- package/dist/lib/db/mikro.js +82 -0
- package/dist/lib/db/mikro.js.map +7 -0
- package/dist/lib/di/container.js +94 -0
- package/dist/lib/di/container.js.map +7 -0
- package/dist/lib/email/send.js +12 -0
- package/dist/lib/email/send.js.map +7 -0
- package/dist/lib/encryption/aes.js +58 -0
- package/dist/lib/encryption/aes.js.map +7 -0
- package/dist/lib/encryption/customFieldValues.js +49 -0
- package/dist/lib/encryption/customFieldValues.js.map +7 -0
- package/dist/lib/encryption/entityFields.js +26 -0
- package/dist/lib/encryption/entityFields.js.map +7 -0
- package/dist/lib/encryption/entityIds.js +80 -0
- package/dist/lib/encryption/entityIds.js.map +7 -0
- package/dist/lib/encryption/find.js +45 -0
- package/dist/lib/encryption/find.js.map +7 -0
- package/dist/lib/encryption/indexDoc.js +69 -0
- package/dist/lib/encryption/indexDoc.js.map +7 -0
- package/dist/lib/encryption/kms.js +282 -0
- package/dist/lib/encryption/kms.js.map +7 -0
- package/dist/lib/encryption/subscriber.js +330 -0
- package/dist/lib/encryption/subscriber.js.map +7 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js +252 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +7 -0
- package/dist/lib/encryption/toggles.js +18 -0
- package/dist/lib/encryption/toggles.js.map +7 -0
- package/dist/lib/entities/naming.js +9 -0
- package/dist/lib/entities/naming.js.map +7 -0
- package/dist/lib/entities/system-entities.js +43 -0
- package/dist/lib/entities/system-entities.js.map +7 -0
- package/dist/lib/frontend/organizationEvents.js +41 -0
- package/dist/lib/frontend/organizationEvents.js.map +7 -0
- package/dist/lib/frontend/useOrganizationScope.js +32 -0
- package/dist/lib/frontend/useOrganizationScope.js.map +7 -0
- package/dist/lib/hotkeys/index.js +128 -0
- package/dist/lib/hotkeys/index.js.map +7 -0
- package/dist/lib/i18n/app-dictionaries.js +17 -0
- package/dist/lib/i18n/app-dictionaries.js.map +7 -0
- package/dist/lib/i18n/config.js +7 -0
- package/dist/lib/i18n/config.js.map +7 -0
- package/dist/lib/i18n/context.js +50 -0
- package/dist/lib/i18n/context.js.map +7 -0
- package/dist/lib/i18n/server.js +68 -0
- package/dist/lib/i18n/server.js.map +7 -0
- package/dist/lib/i18n/translate.js +45 -0
- package/dist/lib/i18n/translate.js.map +7 -0
- package/dist/lib/indexers/error-log.js +82 -0
- package/dist/lib/indexers/error-log.js.map +7 -0
- package/dist/lib/indexers/status-log.js +80 -0
- package/dist/lib/indexers/status-log.js.map +7 -0
- package/dist/lib/lib/auth/jwt.js +34 -0
- package/dist/lib/lib/auth/jwt.js.map +7 -0
- package/dist/lib/lib/auth/server.js +77 -0
- package/dist/lib/lib/auth/server.js.map +7 -0
- package/dist/lib/lib/email/send.js +12 -0
- package/dist/lib/lib/email/send.js.map +7 -0
- package/dist/lib/lib/i18n/config.js +7 -0
- package/dist/lib/lib/i18n/config.js.map +7 -0
- package/dist/lib/lib/i18n/context.js +31 -0
- package/dist/lib/lib/i18n/context.js.map +7 -0
- package/dist/lib/lib/utils.js +9 -0
- package/dist/lib/lib/utils.js.map +7 -0
- package/dist/lib/location/countries.js +68 -0
- package/dist/lib/location/countries.js.map +7 -0
- package/dist/lib/modules/index.js +6 -0
- package/dist/lib/modules/index.js.map +7 -0
- package/dist/lib/modules/registry.js +18 -0
- package/dist/lib/modules/registry.js.map +7 -0
- package/dist/lib/openapi/crud.js +137 -0
- package/dist/lib/openapi/crud.js.map +7 -0
- package/dist/lib/openapi/generator.js +1131 -0
- package/dist/lib/openapi/generator.js.map +7 -0
- package/dist/lib/openapi/index.js +10 -0
- package/dist/lib/openapi/index.js.map +7 -0
- package/dist/lib/openapi/sanitize.js +110 -0
- package/dist/lib/openapi/sanitize.js.map +7 -0
- package/dist/lib/openapi/types.js +1 -0
- package/dist/lib/openapi/types.js.map +7 -0
- package/dist/lib/profiler/index.js +258 -0
- package/dist/lib/profiler/index.js.map +7 -0
- package/dist/lib/query/engine.js +729 -0
- package/dist/lib/query/engine.js.map +7 -0
- package/dist/lib/query/join-utils.js +195 -0
- package/dist/lib/query/join-utils.js.map +7 -0
- package/dist/lib/query/types.js +9 -0
- package/dist/lib/query/types.js.map +7 -0
- package/dist/lib/search/config.js +32 -0
- package/dist/lib/search/config.js.map +7 -0
- package/dist/lib/search/tokenize.js +34 -0
- package/dist/lib/search/tokenize.js.map +7 -0
- package/dist/lib/slugify.js +24 -0
- package/dist/lib/slugify.js.map +7 -0
- package/dist/lib/testing/bootstrap.js +51 -0
- package/dist/lib/testing/bootstrap.js.map +7 -0
- package/dist/lib/testing/index.js +17 -0
- package/dist/lib/testing/index.js.map +7 -0
- package/dist/lib/testing/renderWithProviders.js +15 -0
- package/dist/lib/testing/renderWithProviders.js.map +7 -0
- package/dist/lib/url.js +12 -0
- package/dist/lib/url.js.map +7 -0
- package/dist/lib/utils.js +13 -0
- package/dist/lib/utils.js.map +7 -0
- package/dist/lib/version.js +7 -0
- package/dist/lib/version.js.map +7 -0
- package/dist/modules/dashboard/widgets.js +1 -0
- package/dist/modules/dashboard/widgets.js.map +7 -0
- package/dist/modules/dsl.js +30 -0
- package/dist/modules/dsl.js.map +7 -0
- package/dist/modules/entities/kinds.js +22 -0
- package/dist/modules/entities/kinds.js.map +7 -0
- package/dist/modules/entities/options.js +26 -0
- package/dist/modules/entities/options.js.map +7 -0
- package/dist/modules/entities/validation.js +102 -0
- package/dist/modules/entities/validation.js.map +7 -0
- package/dist/modules/entities/validators.js +88 -0
- package/dist/modules/entities/validators.js.map +7 -0
- package/dist/modules/entities.js +1 -0
- package/dist/modules/entities.js.map +7 -0
- package/dist/modules/navigation/sidebarPreferences.js +50 -0
- package/dist/modules/navigation/sidebarPreferences.js.map +7 -0
- package/dist/modules/perspectives/types.js +1 -0
- package/dist/modules/perspectives/types.js.map +7 -0
- package/dist/modules/registry.js +96 -0
- package/dist/modules/registry.js.map +7 -0
- package/dist/modules/search.js +15 -0
- package/dist/modules/search.js.map +7 -0
- package/dist/modules/vector.js +1 -0
- package/dist/modules/vector.js.map +7 -0
- package/dist/modules/widgets/injection-loader.js +180 -0
- package/dist/modules/widgets/injection-loader.js.map +7 -0
- package/dist/modules/widgets/injection.js +1 -0
- package/dist/modules/widgets/injection.js.map +7 -0
- package/dist/security/features.js +23 -0
- package/dist/security/features.js.map +7 -0
- package/dist/types/pg.d.js +1 -0
- package/dist/types/pg.d.js.map +7 -0
- package/dist/types/react-email.d.js +1 -0
- package/dist/types/react-email.d.js.map +7 -0
- package/dist/types/resend.d.js +1 -0
- package/dist/types/resend.d.js.map +7 -0
- package/jest.config.cjs +22 -0
- package/package.json +88 -0
- package/src/index.ts +0 -0
- package/src/lib/api/__tests__/scoped.test.ts +38 -0
- package/src/lib/api/crud.ts +59 -0
- package/src/lib/api/scoped.ts +239 -0
- package/src/lib/auth/jwt.ts +39 -0
- package/src/lib/auth/server.ts +199 -0
- package/src/lib/boolean.ts +17 -0
- package/src/lib/bootstrap/appResolver.ts +85 -0
- package/src/lib/bootstrap/dynamicLoader.ts +177 -0
- package/src/lib/bootstrap/factory.ts +108 -0
- package/src/lib/bootstrap/index.ts +23 -0
- package/src/lib/bootstrap/types.ts +31 -0
- package/src/lib/cache/segments.ts +56 -0
- package/src/lib/cli/progress.ts +55 -0
- package/src/lib/commands/__tests__/command-bus.test.ts +84 -0
- package/src/lib/commands/__tests__/helpers.test.ts +42 -0
- package/src/lib/commands/command-bus.ts +349 -0
- package/src/lib/commands/customFieldSnapshots.ts +86 -0
- package/src/lib/commands/helpers.ts +143 -0
- package/src/lib/commands/index.ts +4 -0
- package/src/lib/commands/operationMetadata.ts +40 -0
- package/src/lib/commands/registry.ts +46 -0
- package/src/lib/commands/scope.ts +59 -0
- package/src/lib/commands/types.ts +63 -0
- package/src/lib/crud/__tests__/crud-factory.test.ts +333 -0
- package/src/lib/crud/__tests__/custom-fields.test.ts +150 -0
- package/src/lib/crud/cache-stats.ts +127 -0
- package/src/lib/crud/cache.ts +205 -0
- package/src/lib/crud/custom-fields-client.ts +54 -0
- package/src/lib/crud/custom-fields.ts +607 -0
- package/src/lib/crud/errors.ts +23 -0
- package/src/lib/crud/exporters.ts +188 -0
- package/src/lib/crud/factory.ts +1622 -0
- package/src/lib/crud/types.ts +29 -0
- package/src/lib/custom-fields/normalize.ts +45 -0
- package/src/lib/data/engine.ts +562 -0
- package/src/lib/db/escapeLikePattern.ts +2 -0
- package/src/lib/db/mikro.ts +100 -0
- package/src/lib/di/container.ts +105 -0
- package/src/lib/email/send.ts +18 -0
- package/src/lib/encryption/__tests__/customFieldValues.test.ts +63 -0
- package/src/lib/encryption/__tests__/indexDoc.test.ts +115 -0
- package/src/lib/encryption/aes.ts +64 -0
- package/src/lib/encryption/customFieldValues.ts +67 -0
- package/src/lib/encryption/entityFields.ts +39 -0
- package/src/lib/encryption/entityIds.ts +107 -0
- package/src/lib/encryption/find.ts +81 -0
- package/src/lib/encryption/indexDoc.ts +104 -0
- package/src/lib/encryption/kms.ts +337 -0
- package/src/lib/encryption/subscriber.ts +416 -0
- package/src/lib/encryption/tenantDataEncryptionService.ts +313 -0
- package/src/lib/encryption/toggles.ts +15 -0
- package/src/lib/entities/naming.ts +6 -0
- package/src/lib/entities/system-entities.ts +43 -0
- package/src/lib/frontend/organizationEvents.ts +55 -0
- package/src/lib/frontend/useOrganizationScope.ts +30 -0
- package/src/lib/hotkeys/index.ts +168 -0
- package/src/lib/i18n/app-dictionaries.ts +18 -0
- package/src/lib/i18n/config.ts +4 -0
- package/src/lib/i18n/context.tsx +66 -0
- package/src/lib/i18n/server.ts +74 -0
- package/src/lib/i18n/translate.ts +54 -0
- package/src/lib/indexers/error-log.ts +106 -0
- package/src/lib/indexers/status-log.ts +119 -0
- package/src/lib/lib/auth/jwt.ts +39 -0
- package/src/lib/lib/auth/server.ts +94 -0
- package/src/lib/lib/email/send.ts +18 -0
- package/src/lib/lib/i18n/config.ts +4 -0
- package/src/lib/lib/i18n/context.tsx +38 -0
- package/src/lib/lib/utils.ts +6 -0
- package/src/lib/location/countries.ts +97 -0
- package/src/lib/modules/index.ts +1 -0
- package/src/lib/modules/registry.ts +18 -0
- package/src/lib/openapi/crud.ts +218 -0
- package/src/lib/openapi/generator.ts +1311 -0
- package/src/lib/openapi/index.ts +4 -0
- package/src/lib/openapi/sanitize.ts +137 -0
- package/src/lib/openapi/types.ts +79 -0
- package/src/lib/profiler/index.ts +371 -0
- package/src/lib/query/__tests__/engine.test.ts +274 -0
- package/src/lib/query/engine.ts +837 -0
- package/src/lib/query/join-utils.ts +238 -0
- package/src/lib/query/types.ts +121 -0
- package/src/lib/search/config.ts +49 -0
- package/src/lib/search/tokenize.ts +45 -0
- package/src/lib/slugify.ts +28 -0
- package/src/lib/testing/bootstrap.ts +124 -0
- package/src/lib/testing/index.ts +15 -0
- package/src/lib/testing/renderWithProviders.tsx +31 -0
- package/src/lib/url.ts +12 -0
- package/src/lib/utils.ts +17 -0
- package/src/lib/version.ts +5 -0
- package/src/modules/__tests__/dsl.test.ts +35 -0
- package/src/modules/__tests__/registry.test.ts +300 -0
- package/src/modules/dashboard/widgets.ts +57 -0
- package/src/modules/dsl.ts +32 -0
- package/src/modules/entities/__tests__/validation.test.ts +52 -0
- package/src/modules/entities/kinds.ts +20 -0
- package/src/modules/entities/options.ts +36 -0
- package/src/modules/entities/validation.ts +118 -0
- package/src/modules/entities/validators.ts +93 -0
- package/src/modules/entities.ts +102 -0
- package/src/modules/navigation/sidebarPreferences.ts +62 -0
- package/src/modules/perspectives/types.ts +40 -0
- package/src/modules/registry.ts +249 -0
- package/src/modules/search.ts +325 -0
- package/src/modules/vector.ts +122 -0
- package/src/modules/widgets/__tests__/injection.test.ts +48 -0
- package/src/modules/widgets/injection-loader.ts +235 -0
- package/src/modules/widgets/injection.ts +120 -0
- package/src/security/features.ts +22 -0
- package/src/types/pg.d.ts +2 -0
- package/src/types/react-email.d.ts +2 -0
- package/src/types/resend.d.ts +2 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import type { ModuleInjectionWidgetEntry } from '../registry'
|
|
2
|
+
import type {
|
|
3
|
+
InjectionWidgetMetadata,
|
|
4
|
+
InjectionWidgetModule,
|
|
5
|
+
InjectionSpotId,
|
|
6
|
+
ModuleInjectionSlot,
|
|
7
|
+
ModuleInjectionTable,
|
|
8
|
+
InjectionWidgetPlacement,
|
|
9
|
+
} from './injection'
|
|
10
|
+
|
|
11
|
+
type LoadedWidgetModule = InjectionWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }
|
|
12
|
+
export type LoadedInjectionWidget = LoadedWidgetModule & {
|
|
13
|
+
moduleId: string
|
|
14
|
+
key: string
|
|
15
|
+
placement?: {
|
|
16
|
+
groupId?: string
|
|
17
|
+
groupLabel?: string
|
|
18
|
+
groupDescription?: string
|
|
19
|
+
column?: 1 | 2
|
|
20
|
+
kind?: 'tab' | 'group' | 'stack'
|
|
21
|
+
[k: string]: unknown
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type WidgetEntry = ModuleInjectionWidgetEntry & { moduleId: string }
|
|
26
|
+
|
|
27
|
+
// Registration pattern for publishable packages
|
|
28
|
+
let _coreInjectionWidgetEntries: ModuleInjectionWidgetEntry[] | null = null
|
|
29
|
+
let _coreInjectionTables: Array<{ moduleId: string; table: ModuleInjectionTable }> | null = null
|
|
30
|
+
|
|
31
|
+
export function registerCoreInjectionWidgets(entries: ModuleInjectionWidgetEntry[]) {
|
|
32
|
+
if (_coreInjectionWidgetEntries !== null && process.env.NODE_ENV === 'development') {
|
|
33
|
+
console.debug('[Bootstrap] Core injection widgets re-registered (this may occur during HMR)')
|
|
34
|
+
}
|
|
35
|
+
_coreInjectionWidgetEntries = entries
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getCoreInjectionWidgets(): ModuleInjectionWidgetEntry[] {
|
|
39
|
+
if (!_coreInjectionWidgetEntries) {
|
|
40
|
+
// On client-side, bootstrap doesn't run - return empty array gracefully
|
|
41
|
+
if (typeof window !== 'undefined') {
|
|
42
|
+
return []
|
|
43
|
+
}
|
|
44
|
+
throw new Error('[Bootstrap] Core injection widgets not registered. Call registerCoreInjectionWidgets() at bootstrap.')
|
|
45
|
+
}
|
|
46
|
+
return _coreInjectionWidgetEntries
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function registerCoreInjectionTables(tables: Array<{ moduleId: string; table: ModuleInjectionTable }>) {
|
|
50
|
+
if (_coreInjectionTables !== null && process.env.NODE_ENV === 'development') {
|
|
51
|
+
console.debug('[Bootstrap] Core injection tables re-registered (this may occur during HMR)')
|
|
52
|
+
}
|
|
53
|
+
_coreInjectionTables = tables
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function getCoreInjectionTables(): Array<{ moduleId: string; table: ModuleInjectionTable }> {
|
|
57
|
+
if (!_coreInjectionTables) {
|
|
58
|
+
// On client-side, bootstrap doesn't run - return empty array gracefully
|
|
59
|
+
if (typeof window !== 'undefined') {
|
|
60
|
+
return []
|
|
61
|
+
}
|
|
62
|
+
throw new Error('[Bootstrap] Core injection tables not registered. Call registerCoreInjectionTables() at bootstrap.')
|
|
63
|
+
}
|
|
64
|
+
return _coreInjectionTables
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let widgetEntriesPromise: Promise<WidgetEntry[]> | null = null
|
|
68
|
+
type TableEntry = {
|
|
69
|
+
widgetId: string
|
|
70
|
+
moduleId: string
|
|
71
|
+
priority: number
|
|
72
|
+
placement?: ModuleInjectionSlot extends infer S
|
|
73
|
+
? S extends { widgetId: string }
|
|
74
|
+
? Omit<S, 'widgetId' | 'priority'>
|
|
75
|
+
: never
|
|
76
|
+
: never
|
|
77
|
+
}
|
|
78
|
+
let injectionTablePromise: Promise<Map<InjectionSpotId, TableEntry[]>> | null = null
|
|
79
|
+
|
|
80
|
+
function isInjectionSlotObject(value: ModuleInjectionSlot): value is InjectionWidgetPlacement & { widgetId: string; priority?: number } {
|
|
81
|
+
return typeof value === 'object' && value !== null && 'widgetId' in value
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Invalidate the widget entries and widget module cache.
|
|
86
|
+
* Call this when the generated registry is updated or modules are reloaded.
|
|
87
|
+
*/
|
|
88
|
+
export function invalidateInjectionWidgetCache() {
|
|
89
|
+
widgetEntriesPromise = null
|
|
90
|
+
injectionTablePromise = null
|
|
91
|
+
widgetCache.clear()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function loadWidgetEntries(): Promise<WidgetEntry[]> {
|
|
95
|
+
if (!widgetEntriesPromise) {
|
|
96
|
+
const promise = Promise.resolve().then(() =>
|
|
97
|
+
getCoreInjectionWidgets().map((entry) => ({
|
|
98
|
+
...entry,
|
|
99
|
+
moduleId: entry.moduleId || 'unknown',
|
|
100
|
+
}))
|
|
101
|
+
)
|
|
102
|
+
widgetEntriesPromise = promise.catch((err) => {
|
|
103
|
+
// Clear cache on error so next call can retry after registration
|
|
104
|
+
if (widgetEntriesPromise === promise) {
|
|
105
|
+
widgetEntriesPromise = null
|
|
106
|
+
}
|
|
107
|
+
throw err
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
return widgetEntriesPromise
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function loadInjectionTable(): Promise<Map<InjectionSpotId, TableEntry[]>> {
|
|
114
|
+
if (!injectionTablePromise) {
|
|
115
|
+
const promise = Promise.resolve().then(() => {
|
|
116
|
+
const list = getCoreInjectionTables()
|
|
117
|
+
const table = new Map<InjectionSpotId, TableEntry[]>()
|
|
118
|
+
|
|
119
|
+
for (const entry of list) {
|
|
120
|
+
const injectionTable = entry.table ?? {}
|
|
121
|
+
for (const [spotId, widgetIds] of Object.entries(injectionTable)) {
|
|
122
|
+
const widgets = Array.isArray(widgetIds) ? widgetIds : [widgetIds]
|
|
123
|
+
const existing = table.get(spotId) ?? []
|
|
124
|
+
for (const widgetEntry of widgets) {
|
|
125
|
+
if (typeof widgetEntry === 'string') {
|
|
126
|
+
existing.push({ widgetId: widgetEntry, moduleId: entry.moduleId, priority: 0 })
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
if (isInjectionSlotObject(widgetEntry)) {
|
|
130
|
+
const { widgetId, priority = 0, ...placement } = widgetEntry
|
|
131
|
+
existing.push({
|
|
132
|
+
widgetId,
|
|
133
|
+
moduleId: entry.moduleId,
|
|
134
|
+
priority: typeof priority === 'number' ? priority : 0,
|
|
135
|
+
placement,
|
|
136
|
+
})
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
table.set(spotId, existing)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (const [spotId, widgets] of table.entries()) {
|
|
145
|
+
table.set(spotId, widgets.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return table
|
|
149
|
+
})
|
|
150
|
+
injectionTablePromise = promise.catch((err) => {
|
|
151
|
+
// Clear cache on error so next call can retry after registration
|
|
152
|
+
if (injectionTablePromise === promise) {
|
|
153
|
+
injectionTablePromise = null
|
|
154
|
+
}
|
|
155
|
+
throw err
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
return injectionTablePromise
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const widgetCache = new Map<string, Promise<LoadedWidgetModule>>()
|
|
162
|
+
|
|
163
|
+
function ensureValidWidgetModule(mod: any, key: string, moduleId: string): LoadedWidgetModule {
|
|
164
|
+
if (!mod || typeof mod !== 'object') {
|
|
165
|
+
throw new Error(`Invalid injection widget module "${key}" from "${moduleId}" (expected object export)`)
|
|
166
|
+
}
|
|
167
|
+
const widget = (mod.default ?? mod) as InjectionWidgetModule<any, any>
|
|
168
|
+
if (!widget || typeof widget !== 'object') {
|
|
169
|
+
throw new Error(`Invalid injection widget export "${key}" from "${moduleId}" (missing default export)`)
|
|
170
|
+
}
|
|
171
|
+
if (!widget.metadata || typeof widget.metadata !== 'object') {
|
|
172
|
+
throw new Error(`Injection widget "${key}" from "${moduleId}" is missing metadata`)
|
|
173
|
+
}
|
|
174
|
+
const { metadata } = widget
|
|
175
|
+
if (typeof metadata.id !== 'string' || metadata.id.length === 0) {
|
|
176
|
+
throw new Error(`Injection widget "${key}" from "${moduleId}" metadata.id must be a non-empty string`)
|
|
177
|
+
}
|
|
178
|
+
if (typeof metadata.title !== 'string' || metadata.title.length === 0) {
|
|
179
|
+
throw new Error(`Injection widget "${metadata.id}" from "${moduleId}" must have a title`)
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
...widget,
|
|
183
|
+
metadata,
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function loadEntry(entry: WidgetEntry): Promise<LoadedWidgetModule> {
|
|
188
|
+
if (!widgetCache.has(entry.key)) {
|
|
189
|
+
const promise = entry.loader()
|
|
190
|
+
.then((mod) => ensureValidWidgetModule(mod, entry.key, entry.moduleId))
|
|
191
|
+
widgetCache.set(entry.key, promise)
|
|
192
|
+
}
|
|
193
|
+
return widgetCache.get(entry.key)!
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export async function loadAllInjectionWidgets(): Promise<LoadedInjectionWidget[]> {
|
|
197
|
+
const widgetEntries = await loadWidgetEntries()
|
|
198
|
+
const loaded = await Promise.all(widgetEntries.map(async (entry) => {
|
|
199
|
+
const widget = await loadEntry(entry)
|
|
200
|
+
return { ...widget, moduleId: entry.moduleId, key: entry.key }
|
|
201
|
+
}))
|
|
202
|
+
const byId = new Map<string, LoadedWidgetModule & { moduleId: string; key: string }>()
|
|
203
|
+
for (const widget of loaded) {
|
|
204
|
+
if (!byId.has(widget.metadata.id)) {
|
|
205
|
+
byId.set(widget.metadata.id, widget)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return Array.from(byId.values())
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function loadInjectionWidgetById(widgetId: string): Promise<LoadedInjectionWidget | null> {
|
|
212
|
+
const widgetEntries = await loadWidgetEntries()
|
|
213
|
+
for (const entry of widgetEntries) {
|
|
214
|
+
const widget = await loadEntry(entry)
|
|
215
|
+
if (widget.metadata.id === widgetId) {
|
|
216
|
+
return { ...widget, moduleId: entry.moduleId, key: entry.key }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return null
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function loadInjectionWidgetsForSpot(spotId: InjectionSpotId): Promise<LoadedInjectionWidget[]> {
|
|
223
|
+
const table = await loadInjectionTable()
|
|
224
|
+
const entries = table.get(spotId) ?? []
|
|
225
|
+
const widgets = await Promise.all(
|
|
226
|
+
entries.map(async ({ widgetId, placement, priority }) => {
|
|
227
|
+
const widget = await loadInjectionWidgetById(widgetId)
|
|
228
|
+
const combinedPlacement = placement
|
|
229
|
+
? { ...placement, priority: typeof priority === 'number' ? priority : 0 }
|
|
230
|
+
: { priority: typeof priority === 'number' ? priority : 0 }
|
|
231
|
+
return widget ? { ...widget, placement: combinedPlacement } : null
|
|
232
|
+
})
|
|
233
|
+
)
|
|
234
|
+
return widgets.filter((w): w is NonNullable<typeof w> => w !== null)
|
|
235
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Widget injection event handlers for lifecycle management
|
|
5
|
+
*/
|
|
6
|
+
export type WidgetInjectionEventHandlers<TContext = unknown, TData = unknown> = {
|
|
7
|
+
/**
|
|
8
|
+
* Called when the widget is first loaded/mounted
|
|
9
|
+
*/
|
|
10
|
+
onLoad?: (context: TContext) => void | Promise<void>
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Called before save action is executed
|
|
14
|
+
* Return false or throw to prevent save. Optionally return an object with
|
|
15
|
+
* `ok`, `message`, and `fieldErrors` to surface a user-facing reason.
|
|
16
|
+
*/
|
|
17
|
+
onBeforeSave?: (data: TData, context: TContext) => WidgetBeforeSaveResult | Promise<WidgetBeforeSaveResult>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Called when save action is triggered
|
|
21
|
+
*/
|
|
22
|
+
onSave?: (data: TData, context: TContext) => void | Promise<void>
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Called after save action completes successfully
|
|
26
|
+
*/
|
|
27
|
+
onAfterSave?: (data: TData, context: TContext) => void | Promise<void>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Metadata for an injection widget
|
|
32
|
+
*/
|
|
33
|
+
export type InjectionWidgetMetadata = {
|
|
34
|
+
id: string
|
|
35
|
+
title: string
|
|
36
|
+
description?: string
|
|
37
|
+
features?: string[]
|
|
38
|
+
priority?: number
|
|
39
|
+
enabled?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Optional placement metadata declared per injection spot entry.
|
|
44
|
+
* Lets hosts render widgets in tabs, grouped cards, or plain stacks.
|
|
45
|
+
*/
|
|
46
|
+
export type InjectionWidgetPlacement = {
|
|
47
|
+
/**
|
|
48
|
+
* Optional stable identifier for grouping (e.g., tab id or card id)
|
|
49
|
+
*/
|
|
50
|
+
groupId?: string
|
|
51
|
+
/**
|
|
52
|
+
* Display label for the group or tab. Falls back to widget title.
|
|
53
|
+
*/
|
|
54
|
+
groupLabel?: string
|
|
55
|
+
/**
|
|
56
|
+
* Optional helper text shown with the widget when rendered as a group.
|
|
57
|
+
*/
|
|
58
|
+
groupDescription?: string
|
|
59
|
+
/**
|
|
60
|
+
* Preferred column for grouped layouts (1 = left, 2 = right)
|
|
61
|
+
*/
|
|
62
|
+
column?: 1 | 2
|
|
63
|
+
/**
|
|
64
|
+
* Rendering hint for hosts that support categorized widgets.
|
|
65
|
+
* - 'tab' → render as a tab with the widget as tab content
|
|
66
|
+
* - 'group' → render inside a grouped card/section
|
|
67
|
+
* - 'stack' → render inline (default)
|
|
68
|
+
*/
|
|
69
|
+
kind?: 'tab' | 'group' | 'stack'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Props passed to injection widget components
|
|
74
|
+
*/
|
|
75
|
+
export type InjectionWidgetComponentProps<TContext = unknown, TData = unknown> = {
|
|
76
|
+
context: TContext
|
|
77
|
+
data?: TData
|
|
78
|
+
onDataChange?: (data: TData) => void
|
|
79
|
+
disabled?: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type WidgetBeforeSaveResult =
|
|
83
|
+
| boolean
|
|
84
|
+
| void
|
|
85
|
+
| {
|
|
86
|
+
ok?: boolean
|
|
87
|
+
message?: string
|
|
88
|
+
fieldErrors?: Record<string, string>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Complete injection widget module definition
|
|
93
|
+
*/
|
|
94
|
+
export type InjectionWidgetModule<TContext = unknown, TData = unknown> = {
|
|
95
|
+
metadata: InjectionWidgetMetadata
|
|
96
|
+
Widget: ComponentType<InjectionWidgetComponentProps<TContext, TData>>
|
|
97
|
+
eventHandlers?: WidgetInjectionEventHandlers<TContext, TData>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Injection spot identifier - uniquely identifies where widgets can be injected
|
|
102
|
+
*/
|
|
103
|
+
export type InjectionSpotId = string
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Injection table entry mapping spot to widget
|
|
107
|
+
*/
|
|
108
|
+
export type InjectionTableEntry = {
|
|
109
|
+
spotId: InjectionSpotId
|
|
110
|
+
widgetId: string
|
|
111
|
+
priority?: number
|
|
112
|
+
placement?: InjectionWidgetPlacement
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type ModuleInjectionSlot = string | (InjectionWidgetPlacement & { widgetId: string; priority?: number })
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Module's injection table - maps spots to widgets
|
|
119
|
+
*/
|
|
120
|
+
export type ModuleInjectionTable = Record<InjectionSpotId, ModuleInjectionSlot | ModuleInjectionSlot[]>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function matchFeature(required: string, granted: string): boolean {
|
|
2
|
+
if (granted === '*') return true
|
|
3
|
+
if (granted.endsWith('.*')) {
|
|
4
|
+
const prefix = granted.slice(0, -2)
|
|
5
|
+
return required === prefix || required.startsWith(prefix + '.')
|
|
6
|
+
}
|
|
7
|
+
return granted === required
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function hasFeature(granted: readonly string[] | undefined, required: string): boolean {
|
|
11
|
+
if (!Array.isArray(granted) || !granted.length) return false
|
|
12
|
+
return granted.some((feature) => matchFeature(required, feature))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function hasAllFeatures(
|
|
16
|
+
granted: readonly string[] | undefined,
|
|
17
|
+
required: readonly string[] | undefined
|
|
18
|
+
): boolean {
|
|
19
|
+
if (!required || required.length === 0) return true
|
|
20
|
+
if (!Array.isArray(granted) || !granted.length) return false
|
|
21
|
+
return required.every((feature) => hasFeature(granted, feature))
|
|
22
|
+
}
|
package/tsconfig.json
ADDED