@spring-systems/core 0.8.0

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.
Files changed (101) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +8 -0
  3. package/README.md +77 -0
  4. package/dist/adapters/index.d.ts +246 -0
  5. package/dist/adapters/index.js +56 -0
  6. package/dist/adapters/index.js.map +1 -0
  7. package/dist/auth/index.d.ts +17 -0
  8. package/dist/auth/index.js +19 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/chunk-5D6XE7NJ.js +16 -0
  11. package/dist/chunk-5D6XE7NJ.js.map +1 -0
  12. package/dist/chunk-EFUBAQCV.js +94 -0
  13. package/dist/chunk-EFUBAQCV.js.map +1 -0
  14. package/dist/chunk-F2SIMWZ5.js +173 -0
  15. package/dist/chunk-F2SIMWZ5.js.map +1 -0
  16. package/dist/chunk-F7WUQJH7.js +399 -0
  17. package/dist/chunk-F7WUQJH7.js.map +1 -0
  18. package/dist/chunk-GON7Q32Q.js +176 -0
  19. package/dist/chunk-GON7Q32Q.js.map +1 -0
  20. package/dist/chunk-GXU75LQX.js +182 -0
  21. package/dist/chunk-GXU75LQX.js.map +1 -0
  22. package/dist/chunk-HFELOXDQ.js +110 -0
  23. package/dist/chunk-HFELOXDQ.js.map +1 -0
  24. package/dist/chunk-KX32MU3I.js +190 -0
  25. package/dist/chunk-KX32MU3I.js.map +1 -0
  26. package/dist/chunk-MEWPYTWC.js +284 -0
  27. package/dist/chunk-MEWPYTWC.js.map +1 -0
  28. package/dist/chunk-N2L4TUC4.js +34 -0
  29. package/dist/chunk-N2L4TUC4.js.map +1 -0
  30. package/dist/chunk-NQQIVCLX.js +47 -0
  31. package/dist/chunk-NQQIVCLX.js.map +1 -0
  32. package/dist/chunk-OSSX443T.js +146 -0
  33. package/dist/chunk-OSSX443T.js.map +1 -0
  34. package/dist/chunk-PT4DIYUK.js +78 -0
  35. package/dist/chunk-PT4DIYUK.js.map +1 -0
  36. package/dist/chunk-QAVWXARR.js +51 -0
  37. package/dist/chunk-QAVWXARR.js.map +1 -0
  38. package/dist/chunk-RRWKDFAB.js +143 -0
  39. package/dist/chunk-RRWKDFAB.js.map +1 -0
  40. package/dist/chunk-RUCXSQEY.js +42 -0
  41. package/dist/chunk-RUCXSQEY.js.map +1 -0
  42. package/dist/chunk-S6RPCN5H.js +64 -0
  43. package/dist/chunk-S6RPCN5H.js.map +1 -0
  44. package/dist/chunk-S7MKRNMI.js +153 -0
  45. package/dist/chunk-S7MKRNMI.js.map +1 -0
  46. package/dist/chunk-SQB4F3EF.js +55 -0
  47. package/dist/chunk-SQB4F3EF.js.map +1 -0
  48. package/dist/chunk-U5OH3GAI.js +399 -0
  49. package/dist/chunk-U5OH3GAI.js.map +1 -0
  50. package/dist/chunk-UDT2RPX2.js +43 -0
  51. package/dist/chunk-UDT2RPX2.js.map +1 -0
  52. package/dist/config/index.d.ts +63 -0
  53. package/dist/config/index.js +109 -0
  54. package/dist/config/index.js.map +1 -0
  55. package/dist/devtools/index.d.ts +54 -0
  56. package/dist/devtools/index.js +67 -0
  57. package/dist/devtools/index.js.map +1 -0
  58. package/dist/errors/index.d.ts +39 -0
  59. package/dist/errors/index.js +21 -0
  60. package/dist/errors/index.js.map +1 -0
  61. package/dist/events/index.d.ts +153 -0
  62. package/dist/events/index.js +12 -0
  63. package/dist/events/index.js.map +1 -0
  64. package/dist/form-types-D3MdGpjA.d.ts +290 -0
  65. package/dist/framework-config-types-DeUbx4bu.d.ts +574 -0
  66. package/dist/i18n/index.d.ts +37 -0
  67. package/dist/i18n/index.js +7 -0
  68. package/dist/i18n/index.js.map +1 -0
  69. package/dist/index.d.ts +9 -0
  70. package/dist/index.js +42 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/instance/index.d.ts +112 -0
  73. package/dist/instance/index.js +37 -0
  74. package/dist/instance/index.js.map +1 -0
  75. package/dist/logger/index.d.ts +44 -0
  76. package/dist/logger/index.js +27 -0
  77. package/dist/logger/index.js.map +1 -0
  78. package/dist/middleware/index.d.ts +91 -0
  79. package/dist/middleware/index.js +23 -0
  80. package/dist/middleware/index.js.map +1 -0
  81. package/dist/middleware-registry-DT002qRd.d.ts +60 -0
  82. package/dist/middleware-types-DVG9C1qJ.d.ts +85 -0
  83. package/dist/plugins/index.d.ts +104 -0
  84. package/dist/plugins/index.js +16 -0
  85. package/dist/plugins/index.js.map +1 -0
  86. package/dist/runtime-env-config-CajOEJCP.d.ts +148 -0
  87. package/dist/security-sanitize-Bb0PExM6.d.ts +9 -0
  88. package/dist/spring-instance-EbUh4mQb.d.ts +119 -0
  89. package/dist/testing/index.d.ts +129 -0
  90. package/dist/testing/index.js +171 -0
  91. package/dist/testing/index.js.map +1 -0
  92. package/dist/types/index.d.ts +85 -0
  93. package/dist/types/index.js +72 -0
  94. package/dist/types/index.js.map +1 -0
  95. package/dist/utils/index.d.ts +143 -0
  96. package/dist/utils/index.js +786 -0
  97. package/dist/utils/index.js.map +1 -0
  98. package/dist/validation/index.d.ts +48 -0
  99. package/dist/validation/index.js +147 -0
  100. package/dist/validation/index.js.map +1 -0
  101. package/package.json +142 -0
@@ -0,0 +1,176 @@
1
+ import {
2
+ registerFallbackMigration
3
+ } from "./chunk-RUCXSQEY.js";
4
+ import {
5
+ tryGetSpringInstance
6
+ } from "./chunk-EFUBAQCV.js";
7
+ import {
8
+ SpringAdapterError
9
+ } from "./chunk-PT4DIYUK.js";
10
+
11
+ // src/adapters/http-adapter.ts
12
+ var noopAdapter = {
13
+ async request() {
14
+ throw new SpringAdapterError(
15
+ "No HttpAdapter configured. Call setHttpAdapter() before making API requests.",
16
+ "HttpAdapter"
17
+ );
18
+ },
19
+ async get() {
20
+ throw new SpringAdapterError(
21
+ "No HttpAdapter configured. Call setHttpAdapter() before making API requests.",
22
+ "HttpAdapter"
23
+ );
24
+ },
25
+ async post() {
26
+ throw new SpringAdapterError(
27
+ "No HttpAdapter configured. Call setHttpAdapter() before making API requests.",
28
+ "HttpAdapter"
29
+ );
30
+ },
31
+ async put() {
32
+ throw new SpringAdapterError(
33
+ "No HttpAdapter configured. Call setHttpAdapter() before making API requests.",
34
+ "HttpAdapter"
35
+ );
36
+ },
37
+ async delete() {
38
+ throw new SpringAdapterError(
39
+ "No HttpAdapter configured. Call setHttpAdapter() before making API requests.",
40
+ "HttpAdapter"
41
+ );
42
+ }
43
+ };
44
+ var fallbackAdapter = noopAdapter;
45
+ var UI_KEY = /* @__PURE__ */ Symbol.for("spring:httpAdapter");
46
+ registerFallbackMigration((instance) => {
47
+ if (fallbackAdapter !== noopAdapter) {
48
+ instance.ui[UI_KEY] = fallbackAdapter;
49
+ }
50
+ });
51
+ function setHttpAdapter(adapter) {
52
+ const instance = tryGetSpringInstance();
53
+ if (instance) {
54
+ instance.ui[UI_KEY] = adapter;
55
+ } else {
56
+ fallbackAdapter = adapter;
57
+ }
58
+ }
59
+ function getHttpAdapter() {
60
+ const instance = tryGetSpringInstance();
61
+ if (instance) {
62
+ return instance.ui[UI_KEY] ?? noopAdapter;
63
+ }
64
+ return fallbackAdapter;
65
+ }
66
+ function clearHttpAdapter() {
67
+ const instance = tryGetSpringInstance();
68
+ if (instance) {
69
+ delete instance.ui[UI_KEY];
70
+ }
71
+ fallbackAdapter = noopAdapter;
72
+ }
73
+
74
+ // src/adapters/license-adapter.ts
75
+ var defaultLicenseInfo = {
76
+ tier: "community",
77
+ features: []
78
+ };
79
+ var noopAdapter2 = {
80
+ async validateLicense() {
81
+ return { valid: false, tier: "community", message: "No license adapter configured" };
82
+ },
83
+ getLicenseInfo() {
84
+ return defaultLicenseInfo;
85
+ },
86
+ isFeatureLicensed() {
87
+ return false;
88
+ }
89
+ };
90
+ var fallbackAdapter2 = noopAdapter2;
91
+ var UI_KEY2 = /* @__PURE__ */ Symbol.for("spring:licenseAdapter");
92
+ registerFallbackMigration((instance) => {
93
+ if (fallbackAdapter2 !== noopAdapter2) {
94
+ instance.ui[UI_KEY2] = fallbackAdapter2;
95
+ }
96
+ });
97
+ function setLicenseAdapter(adapter) {
98
+ const instance = tryGetSpringInstance();
99
+ if (instance) {
100
+ instance.ui[UI_KEY2] = adapter;
101
+ } else {
102
+ fallbackAdapter2 = adapter;
103
+ }
104
+ }
105
+ function getLicenseAdapter() {
106
+ const instance = tryGetSpringInstance();
107
+ if (instance) {
108
+ return instance.ui[UI_KEY2] ?? noopAdapter2;
109
+ }
110
+ return fallbackAdapter2;
111
+ }
112
+ function clearLicenseAdapter() {
113
+ const instance = tryGetSpringInstance();
114
+ if (instance) {
115
+ delete instance.ui[UI_KEY2];
116
+ }
117
+ fallbackAdapter2 = noopAdapter2;
118
+ }
119
+ function validateLicense() {
120
+ return getLicenseAdapter().validateLicense();
121
+ }
122
+ function isFeatureLicensed(feature) {
123
+ return getLicenseAdapter().isFeatureLicensed(feature);
124
+ }
125
+ function getLicenseInfo() {
126
+ return getLicenseAdapter().getLicenseInfo();
127
+ }
128
+
129
+ // src/adapters/notification-adapter.ts
130
+ var fallback = {
131
+ error: (msg) => console.error("[Notification]", msg),
132
+ success: (msg) => console.info("[Notification]", msg),
133
+ warning: (msg) => console.warn("[Notification]", msg),
134
+ info: (msg) => console.info("[Notification]", msg)
135
+ };
136
+ var fallbackAdapter3 = fallback;
137
+ registerFallbackMigration((instance) => {
138
+ if (fallbackAdapter3 !== fallback) {
139
+ instance.core.notificationAdapter = fallbackAdapter3;
140
+ }
141
+ });
142
+ function setNotificationAdapter(a) {
143
+ const instance = tryGetSpringInstance();
144
+ if (instance) {
145
+ instance.core.notificationAdapter = a;
146
+ } else {
147
+ fallbackAdapter3 = a;
148
+ }
149
+ }
150
+ function getNotificationAdapter() {
151
+ const instance = tryGetSpringInstance();
152
+ return instance ? instance.core.notificationAdapter : fallbackAdapter3;
153
+ }
154
+ function clearNotificationAdapter() {
155
+ const instance = tryGetSpringInstance();
156
+ if (instance) {
157
+ instance.core.notificationAdapter = fallback;
158
+ }
159
+ fallbackAdapter3 = fallback;
160
+ }
161
+
162
+ export {
163
+ setHttpAdapter,
164
+ getHttpAdapter,
165
+ clearHttpAdapter,
166
+ setLicenseAdapter,
167
+ getLicenseAdapter,
168
+ clearLicenseAdapter,
169
+ validateLicense,
170
+ isFeatureLicensed,
171
+ getLicenseInfo,
172
+ setNotificationAdapter,
173
+ getNotificationAdapter,
174
+ clearNotificationAdapter
175
+ };
176
+ //# sourceMappingURL=chunk-GON7Q32Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/http-adapter.ts","../src/adapters/license-adapter.ts","../src/adapters/notification-adapter.ts"],"sourcesContent":["/**\n * HTTP adapter — decouples the framework from any specific HTTP client (axios, fetch, etc.).\n *\n * Consumer applications provide their own HTTP implementation by setting a custom adapter.\n * The framework uses this adapter for all API communication (lists, forms, auth, etc.)\n *\n * @example\n * ```ts\n * import { setHttpAdapter } from \"@spring-systems/core/adapters\";\n *\n * setHttpAdapter({\n * async request(config) {\n * const resp = await fetch(config.url, {\n * method: config.method ?? \"GET\",\n * body: config.data ? JSON.stringify(config.data) : undefined,\n * headers: config.headers,\n * signal: config.signal,\n * });\n * return resp.json();\n * },\n * async get(url, config) {\n * return this.request({ ...config, url, method: \"GET\" });\n * },\n * async post(config) {\n * return this.request({ ...config, method: \"POST\" });\n * },\n * async put(config) {\n * return this.request({ ...config, method: \"PUT\" });\n * },\n * async delete(url, config) {\n * return this.request({ ...config, url, method: \"DELETE\" });\n * },\n * });\n * ```\n *\n * @module http-adapter\n */\n\nimport { SpringAdapterError } from \"../errors/errors\";\nimport { tryGetSpringInstance } from \"../instance/current-instance\";\nimport { registerFallbackMigration } from \"../instance/fallback-bridge\";\n\nexport interface HttpRequest {\n url: string;\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n data?: unknown;\n params?: Record<string, string>;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n /** Override the global timeout for this request (e.g. longer timeout for exports/uploads). */\n timeoutMs?: number;\n suppressToast?: boolean;\n suppressRedirect?: boolean;\n}\n\nexport interface HttpAdapter {\n request<TResponse>(config: HttpRequest): Promise<TResponse | undefined>;\n get<TResponse>(url: string, config?: Partial<HttpRequest>): Promise<TResponse | undefined>;\n post<TData, TResponse>(config: HttpRequest & { data: TData }): Promise<TResponse | undefined>;\n put<TData, TResponse>(config: HttpRequest & { data: TData }): Promise<TResponse | undefined>;\n delete<TResponse>(url: string, config?: Partial<HttpRequest>): Promise<TResponse | undefined>;\n}\n\n/**\n * Default adapter — throws at runtime so missing adapter is caught early.\n * Consumer apps must provide a real HTTP adapter before making API calls.\n */\nconst noopAdapter = {\n async request() {\n throw new SpringAdapterError(\n \"No HttpAdapter configured. Call setHttpAdapter() before making API requests.\",\n \"HttpAdapter\",\n );\n },\n async get() {\n throw new SpringAdapterError(\n \"No HttpAdapter configured. Call setHttpAdapter() before making API requests.\",\n \"HttpAdapter\",\n );\n },\n async post() {\n throw new SpringAdapterError(\n \"No HttpAdapter configured. Call setHttpAdapter() before making API requests.\",\n \"HttpAdapter\",\n );\n },\n async put() {\n throw new SpringAdapterError(\n \"No HttpAdapter configured. Call setHttpAdapter() before making API requests.\",\n \"HttpAdapter\",\n );\n },\n async delete() {\n throw new SpringAdapterError(\n \"No HttpAdapter configured. Call setHttpAdapter() before making API requests.\",\n \"HttpAdapter\",\n );\n },\n} satisfies HttpAdapter;\n\nlet fallbackAdapter: HttpAdapter = noopAdapter;\n\nconst UI_KEY = Symbol.for(\"spring:httpAdapter\");\n\n// Migrate fallback HTTP adapter to SpringInstance when SpringProvider mounts\nregisterFallbackMigration((instance) => {\n if (fallbackAdapter !== noopAdapter) {\n instance.ui[UI_KEY] = fallbackAdapter;\n }\n});\n\nexport function setHttpAdapter(adapter: HttpAdapter): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.ui[UI_KEY] = adapter;\n } else {\n fallbackAdapter = adapter;\n }\n}\n\nexport function getHttpAdapter(): HttpAdapter {\n const instance = tryGetSpringInstance();\n if (instance) {\n return (instance.ui[UI_KEY] as HttpAdapter) ?? noopAdapter;\n }\n return fallbackAdapter;\n}\n\n/**\n * Reset the HTTP adapter to the default noop adapter.\n * For testing only.\n */\nexport function clearHttpAdapter(): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n delete instance.ui[UI_KEY];\n }\n fallbackAdapter = noopAdapter;\n}\n","/**\n * License adapter — provides a pluggable interface for enterprise license validation.\n *\n * Enterprise clients integrate with their license servers by providing a custom adapter.\n * The framework checks license status at key points (startup, feature gating, etc.)\n *\n * @example\n * ```ts\n * import { setLicenseAdapter } from \"@spring-systems/core/adapters\";\n *\n * setLicenseAdapter({\n * async validateLicense() {\n * const resp = await fetch(\"/api/license\");\n * const data = await resp.json();\n * return { valid: data.active, expiresAt: new Date(data.expiresAt), tier: data.tier };\n * },\n * getLicenseInfo() {\n * return cachedLicenseInfo;\n * },\n * isFeatureLicensed(feature) {\n * return cachedLicenseInfo.features.includes(feature);\n * },\n * });\n * ```\n *\n * @module license-adapter\n */\n\nimport { tryGetSpringInstance } from \"../instance/current-instance\";\nimport { registerFallbackMigration } from \"../instance/fallback-bridge\";\n\n/** Result of a license validation call */\nexport interface LicenseValidationResult {\n /** Whether the license is currently valid */\n valid: boolean;\n /** When the license expires (undefined = perpetual) */\n expiresAt?: Date;\n /** License tier identifier (e.g. \"community\", \"professional\", \"enterprise\") */\n tier?: string;\n /** Human-readable message (e.g. \"License expired\", \"Trial period\") */\n message?: string;\n}\n\n/** Cached license info (synchronous access) */\nexport interface LicenseInfo {\n /** License tier (e.g. \"standard\", \"professional\", \"enterprise\") */\n tier: string;\n /** List of licensed feature keys */\n features: string[];\n /** When the license expires */\n expiresAt?: Date;\n}\n\nexport interface LicenseAdapter {\n /**\n * Validate the license asynchronously (e.g. call a license server).\n * Called at application startup and periodically.\n */\n validateLicense(): Promise<LicenseValidationResult>;\n\n /**\n * Get cached license info synchronously.\n * Returns info from the last successful validation.\n */\n getLicenseInfo(): LicenseInfo;\n\n /**\n * Check if a specific feature is licensed.\n * Uses cached license info for synchronous access.\n */\n isFeatureLicensed(feature: string): boolean;\n}\n\nconst defaultLicenseInfo = {\n tier: \"community\",\n features: [],\n} satisfies LicenseInfo;\n\n/**\n * Default adapter — fail-closed: no features licensed, validation returns invalid.\n * Enterprise clients must provide a real adapter to unlock licensed features.\n * This ensures that a missing adapter never accidentally grants access.\n */\nconst noopAdapter = {\n async validateLicense() {\n return { valid: false, tier: \"community\", message: \"No license adapter configured\" };\n },\n getLicenseInfo() {\n return defaultLicenseInfo;\n },\n isFeatureLicensed() {\n return false;\n },\n} satisfies LicenseAdapter;\n\nlet fallbackAdapter: LicenseAdapter = noopAdapter;\n\nconst UI_KEY = Symbol.for(\"spring:licenseAdapter\");\n\n// Migrate fallback license adapter to SpringInstance when SpringProvider mounts\nregisterFallbackMigration((instance) => {\n if (fallbackAdapter !== noopAdapter) {\n instance.ui[UI_KEY] = fallbackAdapter;\n }\n});\n\nexport function setLicenseAdapter(adapter: LicenseAdapter): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.ui[UI_KEY] = adapter;\n } else {\n fallbackAdapter = adapter;\n }\n}\n\nexport function getLicenseAdapter(): LicenseAdapter {\n const instance = tryGetSpringInstance();\n if (instance) {\n return (instance.ui[UI_KEY] as LicenseAdapter) ?? noopAdapter;\n }\n return fallbackAdapter;\n}\n\n/**\n * Reset the license adapter to the default noop (fail-closed) adapter.\n * For testing only.\n */\nexport function clearLicenseAdapter(): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n delete instance.ui[UI_KEY];\n }\n fallbackAdapter = noopAdapter;\n}\n\n/**\n * Convenience: validate the license via the configured adapter.\n */\nexport function validateLicense(): Promise<LicenseValidationResult> {\n return getLicenseAdapter().validateLicense();\n}\n\n/**\n * Convenience: check if a feature is licensed.\n */\nexport function isFeatureLicensed(feature: string): boolean {\n return getLicenseAdapter().isFeatureLicensed(feature);\n}\n\n/**\n * Convenience: get cached license info.\n */\nexport function getLicenseInfo(): LicenseInfo {\n return getLicenseAdapter().getLicenseInfo();\n}\n","/**\n * Notification adapter — decouples API/validation layers from the UI notification system (react-toastify).\n * Pattern matches setLogAdapter() in logger.ts.\n * @module notification-adapter\n */\n\nimport { tryGetSpringInstance } from \"../instance/current-instance\";\nimport { registerFallbackMigration } from \"../instance/fallback-bridge\";\n\nexport interface NotificationAdapter {\n error(msg: string): void;\n success(msg: string): void;\n warning(msg: string): void;\n info(msg: string): void;\n}\n\nconst fallback = {\n error: (msg) => console.error(\"[Notification]\", msg),\n success: (msg) => console.info(\"[Notification]\", msg),\n warning: (msg) => console.warn(\"[Notification]\", msg),\n info: (msg) => console.info(\"[Notification]\", msg),\n} satisfies NotificationAdapter;\n\nlet fallbackAdapter: NotificationAdapter = fallback;\n\n// Migrate fallback notification adapter to SpringInstance when SpringProvider mounts\nregisterFallbackMigration((instance) => {\n if (fallbackAdapter !== fallback) {\n instance.core.notificationAdapter = fallbackAdapter;\n }\n});\n\nexport function setNotificationAdapter(a: NotificationAdapter): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.core.notificationAdapter = a;\n } else {\n fallbackAdapter = a;\n }\n}\n\nexport function getNotificationAdapter(): NotificationAdapter {\n const instance = tryGetSpringInstance();\n return instance ? instance.core.notificationAdapter : fallbackAdapter;\n}\n\n/** Reset to default notification adapter. For testing/integration teardown only. */\nexport function clearNotificationAdapter(): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.core.notificationAdapter = fallback;\n }\n fallbackAdapter = fallback;\n}\n"],"mappings":";;;;;;;;;;;AAmEA,IAAM,cAAc;AAAA,EAChB,MAAM,UAAU;AACZ,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EACA,MAAM,MAAM;AACR,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EACA,MAAM,OAAO;AACT,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EACA,MAAM,MAAM;AACR,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EACA,MAAM,SAAS;AACX,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,IAAI,kBAA+B;AAEnC,IAAM,SAAS,uBAAO,IAAI,oBAAoB;AAG9C,0BAA0B,CAAC,aAAa;AACpC,MAAI,oBAAoB,aAAa;AACjC,aAAS,GAAG,MAAM,IAAI;AAAA,EAC1B;AACJ,CAAC;AAEM,SAAS,eAAe,SAA4B;AACvD,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,aAAS,GAAG,MAAM,IAAI;AAAA,EAC1B,OAAO;AACH,sBAAkB;AAAA,EACtB;AACJ;AAEO,SAAS,iBAA8B;AAC1C,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,WAAQ,SAAS,GAAG,MAAM,KAAqB;AAAA,EACnD;AACA,SAAO;AACX;AAMO,SAAS,mBAAyB;AACrC,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,WAAO,SAAS,GAAG,MAAM;AAAA,EAC7B;AACA,oBAAkB;AACtB;;;ACjEA,IAAM,qBAAqB;AAAA,EACvB,MAAM;AAAA,EACN,UAAU,CAAC;AACf;AAOA,IAAMA,eAAc;AAAA,EAChB,MAAM,kBAAkB;AACpB,WAAO,EAAE,OAAO,OAAO,MAAM,aAAa,SAAS,gCAAgC;AAAA,EACvF;AAAA,EACA,iBAAiB;AACb,WAAO;AAAA,EACX;AAAA,EACA,oBAAoB;AAChB,WAAO;AAAA,EACX;AACJ;AAEA,IAAIC,mBAAkCD;AAEtC,IAAME,UAAS,uBAAO,IAAI,uBAAuB;AAGjD,0BAA0B,CAAC,aAAa;AACpC,MAAID,qBAAoBD,cAAa;AACjC,aAAS,GAAGE,OAAM,IAAID;AAAA,EAC1B;AACJ,CAAC;AAEM,SAAS,kBAAkB,SAA+B;AAC7D,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,aAAS,GAAGC,OAAM,IAAI;AAAA,EAC1B,OAAO;AACH,IAAAD,mBAAkB;AAAA,EACtB;AACJ;AAEO,SAAS,oBAAoC;AAChD,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,WAAQ,SAAS,GAAGC,OAAM,KAAwBF;AAAA,EACtD;AACA,SAAOC;AACX;AAMO,SAAS,sBAA4B;AACxC,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,WAAO,SAAS,GAAGC,OAAM;AAAA,EAC7B;AACA,EAAAD,mBAAkBD;AACtB;AAKO,SAAS,kBAAoD;AAChE,SAAO,kBAAkB,EAAE,gBAAgB;AAC/C;AAKO,SAAS,kBAAkB,SAA0B;AACxD,SAAO,kBAAkB,EAAE,kBAAkB,OAAO;AACxD;AAKO,SAAS,iBAA8B;AAC1C,SAAO,kBAAkB,EAAE,eAAe;AAC9C;;;AC1IA,IAAM,WAAW;AAAA,EACb,OAAO,CAAC,QAAQ,QAAQ,MAAM,kBAAkB,GAAG;AAAA,EACnD,SAAS,CAAC,QAAQ,QAAQ,KAAK,kBAAkB,GAAG;AAAA,EACpD,SAAS,CAAC,QAAQ,QAAQ,KAAK,kBAAkB,GAAG;AAAA,EACpD,MAAM,CAAC,QAAQ,QAAQ,KAAK,kBAAkB,GAAG;AACrD;AAEA,IAAIG,mBAAuC;AAG3C,0BAA0B,CAAC,aAAa;AACpC,MAAIA,qBAAoB,UAAU;AAC9B,aAAS,KAAK,sBAAsBA;AAAA,EACxC;AACJ,CAAC;AAEM,SAAS,uBAAuB,GAA8B;AACjE,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,aAAS,KAAK,sBAAsB;AAAA,EACxC,OAAO;AACH,IAAAA,mBAAkB;AAAA,EACtB;AACJ;AAEO,SAAS,yBAA8C;AAC1D,QAAM,WAAW,qBAAqB;AACtC,SAAO,WAAW,SAAS,KAAK,sBAAsBA;AAC1D;AAGO,SAAS,2BAAiC;AAC7C,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,aAAS,KAAK,sBAAsB;AAAA,EACxC;AACA,EAAAA,mBAAkB;AACtB;","names":["noopAdapter","fallbackAdapter","UI_KEY","fallbackAdapter"]}
@@ -0,0 +1,182 @@
1
+ import {
2
+ getRegisteredCapabilityNames,
3
+ hasCapability
4
+ } from "./chunk-S7MKRNMI.js";
5
+ import {
6
+ logError,
7
+ logWarn
8
+ } from "./chunk-KX32MU3I.js";
9
+ import {
10
+ devThrow,
11
+ devWarn
12
+ } from "./chunk-EFUBAQCV.js";
13
+ import {
14
+ SpringPluginError
15
+ } from "./chunk-PT4DIYUK.js";
16
+
17
+ // src/plugins/plugin.ts
18
+ function isDescriptor(plugin) {
19
+ return typeof plugin === "object" && plugin !== null && "setup" in plugin;
20
+ }
21
+ function validateDescriptor(plugin, index) {
22
+ if (!plugin.name || typeof plugin.name !== "string" || plugin.name.trim().length === 0) {
23
+ devWarn("Plugin", `Plugin[${index}]: descriptor.name must be a non-empty string`);
24
+ return false;
25
+ }
26
+ if (typeof plugin.setup !== "function") {
27
+ devWarn("Plugin", `Plugin "${plugin.name}": descriptor.setup must be a function`);
28
+ return false;
29
+ }
30
+ return true;
31
+ }
32
+ function getPluginName(plugin, index) {
33
+ return isDescriptor(plugin) ? plugin.name : `plugin[${index}]`;
34
+ }
35
+ function topologicalSort(plugins) {
36
+ const descriptors = [];
37
+ const functions = [];
38
+ for (const p of plugins) {
39
+ if (isDescriptor(p)) {
40
+ descriptors.push(p);
41
+ } else {
42
+ functions.push(p);
43
+ }
44
+ }
45
+ if (descriptors.every((d) => !d.requires || d.requires.length === 0)) {
46
+ return plugins;
47
+ }
48
+ const nameMap = /* @__PURE__ */ new Map();
49
+ for (const d of descriptors) {
50
+ if (nameMap.has(d.name)) {
51
+ throw new SpringPluginError(
52
+ `Duplicate plugin name "${d.name}". Each plugin must have a unique name. If you need to override a plugin, remove the original from the plugins array first.`,
53
+ d.name
54
+ );
55
+ }
56
+ nameMap.set(d.name, d);
57
+ }
58
+ const inDegree = /* @__PURE__ */ new Map();
59
+ const adjacency = /* @__PURE__ */ new Map();
60
+ for (const d of descriptors) {
61
+ if (!inDegree.has(d.name)) inDegree.set(d.name, 0);
62
+ if (!adjacency.has(d.name)) adjacency.set(d.name, []);
63
+ for (const req of d.requires ?? []) {
64
+ if (!nameMap.has(req)) {
65
+ devThrow("Plugin", `Plugin "${d.name}" requires "${req}" which is not registered. Add "${req}" to the plugins array.`);
66
+ continue;
67
+ }
68
+ if (!adjacency.has(req)) adjacency.set(req, []);
69
+ const deps = adjacency.get(req);
70
+ if (!deps) continue;
71
+ deps.push(d.name);
72
+ inDegree.set(d.name, (inDegree.get(d.name) ?? 0) + 1);
73
+ }
74
+ }
75
+ const queue = [];
76
+ for (const [name, deg] of inDegree) {
77
+ if (deg === 0) queue.push(name);
78
+ }
79
+ const sorted = [];
80
+ while (queue.length > 0) {
81
+ const name = queue.shift();
82
+ if (!name) break;
83
+ const desc = nameMap.get(name);
84
+ if (desc) sorted.push(desc);
85
+ for (const neighbor of adjacency.get(name) ?? []) {
86
+ const deg = (inDegree.get(neighbor) ?? 1) - 1;
87
+ inDegree.set(neighbor, deg);
88
+ if (deg === 0) queue.push(neighbor);
89
+ }
90
+ }
91
+ if (sorted.length < descriptors.length) {
92
+ const missing = descriptors.filter((d) => !sorted.includes(d)).map((d) => d.name);
93
+ throw new SpringPluginError(
94
+ `Circular dependency detected among plugins: ${missing.join(", ")}. Review the 'requires' arrays to break the cycle.`,
95
+ missing[0] ?? "unknown"
96
+ );
97
+ }
98
+ return [...functions, ...sorted];
99
+ }
100
+ var CLEANUP_KEY = /* @__PURE__ */ Symbol.for("spring:pluginCleanups");
101
+ var LOADED_KEY = /* @__PURE__ */ Symbol.for("spring:loadedPlugins");
102
+ function applyPlugins(instance, plugins) {
103
+ const sorted = topologicalSort(plugins);
104
+ const cleanups = [];
105
+ const loaded = [];
106
+ for (let i = 0; i < sorted.length; i++) {
107
+ const plugin = sorted[i];
108
+ if (!plugin) continue;
109
+ if (typeof plugin !== "function" && !isDescriptor(plugin)) {
110
+ devWarn("Plugin", `Plugin[${i}] is neither a function nor a valid descriptor \u2014 skipping`);
111
+ continue;
112
+ }
113
+ if (isDescriptor(plugin) && !validateDescriptor(plugin, i)) {
114
+ continue;
115
+ }
116
+ const name = getPluginName(plugin, i);
117
+ if (isDescriptor(plugin) && plugin.capabilities) {
118
+ const registeredNames = getRegisteredCapabilityNames();
119
+ const unregistered = plugin.capabilities.filter(
120
+ (c) => !registeredNames.includes(c)
121
+ );
122
+ if (unregistered.length > 0) {
123
+ devWarn(
124
+ "Plugin",
125
+ `Plugin "${name}" references unregistered capabilities: ${unregistered.join(", ")}. Did you forget to call configureCapabilities()? Registered: [${registeredNames.join(", ")}]`
126
+ );
127
+ }
128
+ const missing = plugin.capabilities.filter((c) => !hasCapability(c));
129
+ if (missing.length > 0) {
130
+ logWarn("Plugin", `Skipping "${name}" \u2014 missing capabilities: ${missing.join(", ")}`);
131
+ continue;
132
+ }
133
+ }
134
+ try {
135
+ const setupFn = isDescriptor(plugin) ? plugin.setup : plugin;
136
+ const setupCleanup = setupFn(instance);
137
+ if (typeof setupCleanup === "function") {
138
+ cleanups.push(setupCleanup);
139
+ } else if (setupCleanup !== void 0) {
140
+ logWarn("Plugin", `Plugin "${name}" returned non-function cleanup; ignoring.`);
141
+ }
142
+ if (isDescriptor(plugin) && plugin.cleanup !== void 0) {
143
+ if (typeof plugin.cleanup === "function") {
144
+ cleanups.push(plugin.cleanup);
145
+ } else {
146
+ logWarn("Plugin", `Plugin "${name}" has non-function descriptor.cleanup; ignoring.`);
147
+ }
148
+ }
149
+ loaded.push({
150
+ name,
151
+ version: isDescriptor(plugin) ? plugin.version : void 0
152
+ });
153
+ } catch (e) {
154
+ logError(`Plugin setup failed: ${name}`, e);
155
+ }
156
+ }
157
+ instance.ui[CLEANUP_KEY] = cleanups;
158
+ instance.ui[LOADED_KEY] = loaded;
159
+ }
160
+ function disposePlugins(instance) {
161
+ const cleanups = instance.ui[CLEANUP_KEY];
162
+ if (!cleanups) return;
163
+ for (const cleanup of cleanups) {
164
+ try {
165
+ cleanup();
166
+ } catch (e) {
167
+ logError("Plugin cleanup failed", e);
168
+ }
169
+ }
170
+ delete instance.ui[CLEANUP_KEY];
171
+ delete instance.ui[LOADED_KEY];
172
+ }
173
+ function getLoadedPlugins(instance) {
174
+ return instance.ui[LOADED_KEY] ?? [];
175
+ }
176
+
177
+ export {
178
+ applyPlugins,
179
+ disposePlugins,
180
+ getLoadedPlugins
181
+ };
182
+ //# sourceMappingURL=chunk-GXU75LQX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugins/plugin.ts"],"sourcesContent":["/**\n * Plugin system for the SPRING framework.\n *\n * A plugin can be either:\n * - A simple setup function (backward compatible): `(instance) => cleanup?`\n * - A plugin descriptor with metadata: `{ name, version, setup, ... }`\n *\n * Plugin descriptors enable:\n * - Dependency declaration and topological ordering\n * - Capability requirements checking\n * - Version compatibility validation\n * - Plugin discovery and introspection\n *\n * @module plugin\n */\n\nimport { getRegisteredCapabilityNames,hasCapability } from \"../config/capabilities\";\nimport { SpringPluginError } from \"../errors/errors\";\nimport type { SpringInstance } from \"../instance/spring-instance\";\nimport { logError, logWarn } from \"../logger/logger\";\nimport type { FormDataValue, FormStoreState } from \"../types/form-types\";\nimport type { ColumnType } from \"../types/grid-types\";\nimport { devThrow, devWarn } from \"../utils/dev-warnings\";\n\n// ---------------------------------------------------------------------------\n// Types for plugin authors (column renderers)\n// ---------------------------------------------------------------------------\n\n/** Column renderer info passed to custom renderers. */\nexport interface PluginColumnRendererInfo {\n field: string;\n type: ColumnType;\n title: string;\n suffix?: string;\n decimals?: number;\n name?: string;\n}\n\n/**\n * Column renderer function signature.\n * Returns a React node (any valid JSX return value).\n */\nexport type PluginColumnRenderer = (\n value: FormDataValue,\n row: FormStoreState,\n column: PluginColumnRendererInfo,\n) => unknown;\n\n// ---------------------------------------------------------------------------\n// Plugin types\n// ---------------------------------------------------------------------------\n\n/** Simple plugin function (backward compatible) */\nexport type SpringPluginFn = (instance: SpringInstance) => void | (() => void);\n\n/**\n * Plugin descriptor with metadata for enterprise plugin management.\n *\n * @example\n * ```ts\n * const auditPlugin: SpringPluginDescriptor = {\n * name: \"@spring-systems/audit\",\n * version: \"1.0.0\",\n * requires: [\"@spring-systems/core-validators\"],\n * capabilities: [\"advancedFiltering\"],\n * setup(instance) {\n * registerMiddleware(\"form:beforeSave\", auditMiddleware);\n * return () => { cleanup(); };\n * },\n * };\n * ```\n */\nexport interface SpringPluginDescriptor {\n /** Unique plugin name. Use npm-style scoped names (e.g. \"@myorg/plugin-name\"). */\n name: string;\n /** Semver version string. */\n version?: string;\n /** Names of plugins that must be loaded before this one. */\n requires?: string[];\n /** Capability keys that must be enabled for this plugin to activate. */\n capabilities?: string[];\n /** Plugin setup function. Returns optional cleanup. */\n setup: SpringPluginFn;\n /**\n * Explicit cleanup function. Called by `disposePlugins()` alongside any\n * cleanup returned from `setup()`. Also available for direct invocation\n * by consumers who hold a reference to the descriptor.\n */\n cleanup?: () => void;\n}\n\n/** A SPRING plugin — either a simple function or a descriptor with metadata. */\nexport type SpringPlugin = SpringPluginFn | SpringPluginDescriptor;\n\nfunction isDescriptor(plugin: SpringPlugin): plugin is SpringPluginDescriptor {\n return typeof plugin === \"object\" && plugin !== null && \"setup\" in plugin;\n}\n\nfunction validateDescriptor(plugin: SpringPluginDescriptor, index: number): boolean {\n if (!plugin.name || typeof plugin.name !== \"string\" || plugin.name.trim().length === 0) {\n devWarn(\"Plugin\", `Plugin[${index}]: descriptor.name must be a non-empty string`);\n return false;\n }\n if (typeof plugin.setup !== \"function\") {\n devWarn(\"Plugin\", `Plugin \"${plugin.name}\": descriptor.setup must be a function`);\n return false;\n }\n return true;\n}\n\nfunction getPluginName(plugin: SpringPlugin, index: number): string {\n return isDescriptor(plugin) ? plugin.name : `plugin[${index}]`;\n}\n\n// ---------------------------------------------------------------------------\n// Topological sort for plugin dependencies\n// ---------------------------------------------------------------------------\n\nfunction topologicalSort(plugins: SpringPlugin[]): SpringPlugin[] {\n // Separate descriptors (with potential deps) from simple functions\n const descriptors: SpringPluginDescriptor[] = [];\n const functions: SpringPluginFn[] = [];\n\n for (const p of plugins) {\n if (isDescriptor(p)) {\n descriptors.push(p);\n } else {\n functions.push(p);\n }\n }\n\n // If no descriptors have dependencies, skip sorting\n if (descriptors.every((d) => !d.requires || d.requires.length === 0)) {\n return plugins;\n }\n\n // Build adjacency map\n const nameMap = new Map<string, SpringPluginDescriptor>();\n for (const d of descriptors) {\n if (nameMap.has(d.name)) {\n throw new SpringPluginError(\n `Duplicate plugin name \"${d.name}\". ` +\n \"Each plugin must have a unique name. If you need to override a plugin, \" +\n \"remove the original from the plugins array first.\",\n d.name,\n );\n }\n nameMap.set(d.name, d);\n }\n\n // Kahn's algorithm for topological sort\n const inDegree = new Map<string, number>();\n const adjacency = new Map<string, string[]>();\n\n for (const d of descriptors) {\n if (!inDegree.has(d.name)) inDegree.set(d.name, 0);\n if (!adjacency.has(d.name)) adjacency.set(d.name, []);\n\n for (const req of d.requires ?? []) {\n if (!nameMap.has(req)) {\n devThrow(\"Plugin\", `Plugin \"${d.name}\" requires \"${req}\" which is not registered. Add \"${req}\" to the plugins array.`);\n continue;\n }\n if (!adjacency.has(req)) adjacency.set(req, []);\n const deps = adjacency.get(req);\n if (!deps) continue;\n deps.push(d.name);\n inDegree.set(d.name, (inDegree.get(d.name) ?? 0) + 1);\n }\n }\n\n const queue: string[] = [];\n for (const [name, deg] of inDegree) {\n if (deg === 0) queue.push(name);\n }\n\n const sorted: SpringPluginDescriptor[] = [];\n while (queue.length > 0) {\n const name = queue.shift();\n if (!name) break;\n const desc = nameMap.get(name);\n if (desc) sorted.push(desc);\n for (const neighbor of adjacency.get(name) ?? []) {\n const deg = (inDegree.get(neighbor) ?? 1) - 1;\n inDegree.set(neighbor, deg);\n if (deg === 0) queue.push(neighbor);\n }\n }\n\n if (sorted.length < descriptors.length) {\n const missing = descriptors.filter((d) => !sorted.includes(d)).map((d) => d.name);\n throw new SpringPluginError(\n `Circular dependency detected among plugins: ${missing.join(\", \")}. ` +\n \"Review the 'requires' arrays to break the cycle.\",\n missing[0] ?? \"unknown\",\n );\n }\n\n // Simple functions first, then sorted descriptors\n return [...functions, ...sorted];\n}\n\n// ---------------------------------------------------------------------------\n// Plugin lifecycle\n// ---------------------------------------------------------------------------\n\nconst CLEANUP_KEY: unique symbol = Symbol.for(\"spring:pluginCleanups\");\nconst LOADED_KEY: unique symbol = Symbol.for(\"spring:loadedPlugins\");\n\ninterface LoadedPluginInfo {\n name: string;\n version?: string;\n}\n\n/**\n * Apply an array of plugins to a SpringInstance.\n * Plugins with dependencies are topologically sorted.\n * Each may return a cleanup function stored on the instance.\n *\n * Setup errors are caught and logged — a failing plugin does not block others.\n *\n * @param instance - The SpringInstance to apply plugins to\n * @param plugins - Array of plugins (simple functions or descriptors with metadata)\n */\nexport function applyPlugins(instance: SpringInstance, plugins: SpringPlugin[]): void {\n const sorted = topologicalSort(plugins);\n const cleanups: (() => void)[] = [];\n const loaded: LoadedPluginInfo[] = [];\n\n for (let i = 0; i < sorted.length; i++) {\n const plugin = sorted[i];\n if (!plugin) continue;\n\n // Validate plugin structure\n if (typeof plugin !== \"function\" && !isDescriptor(plugin)) {\n devWarn(\"Plugin\", `Plugin[${i}] is neither a function nor a valid descriptor — skipping`);\n continue;\n }\n if (isDescriptor(plugin) && !validateDescriptor(plugin, i)) {\n continue;\n }\n\n const name = getPluginName(plugin, i);\n\n // Check capability requirements\n if (isDescriptor(plugin) && plugin.capabilities) {\n // Warn about capabilities that haven't been registered at all\n const registeredNames = getRegisteredCapabilityNames();\n const unregistered = plugin.capabilities.filter(\n (c) => !registeredNames.includes(c),\n );\n if (unregistered.length > 0) {\n devWarn(\n \"Plugin\",\n `Plugin \"${name}\" references unregistered capabilities: ${unregistered.join(\", \")}. ` +\n `Did you forget to call configureCapabilities()? Registered: [${registeredNames.join(\", \")}]`,\n );\n }\n\n const missing = plugin.capabilities.filter((c) => !hasCapability(c as keyof import(\"../config/capabilities\").CapabilityMap));\n if (missing.length > 0) {\n logWarn(\"Plugin\", `Skipping \"${name}\" — missing capabilities: ${missing.join(\", \")}`);\n continue;\n }\n }\n\n try {\n const setupFn = isDescriptor(plugin) ? plugin.setup : plugin;\n const setupCleanup = setupFn(instance);\n if (typeof setupCleanup === \"function\") {\n cleanups.push(setupCleanup);\n } else if (setupCleanup !== undefined) {\n logWarn(\"Plugin\", `Plugin \"${name}\" returned non-function cleanup; ignoring.`);\n }\n if (isDescriptor(plugin) && plugin.cleanup !== undefined) {\n if (typeof plugin.cleanup === \"function\") {\n cleanups.push(plugin.cleanup);\n } else {\n logWarn(\"Plugin\", `Plugin \"${name}\" has non-function descriptor.cleanup; ignoring.`);\n }\n }\n loaded.push({\n name,\n version: isDescriptor(plugin) ? plugin.version : undefined,\n });\n } catch (e: unknown) {\n logError(`Plugin setup failed: ${name}`, e);\n }\n }\n\n instance.ui[CLEANUP_KEY] = cleanups;\n instance.ui[LOADED_KEY] = loaded;\n}\n\n/**\n * Run all cleanup functions stored by `applyPlugins()` and remove them\n * from the instance. Errors during cleanup are caught and logged.\n *\n * @param instance - The SpringInstance whose plugins should be disposed\n */\nexport function disposePlugins(instance: SpringInstance): void {\n const cleanups = instance.ui[CLEANUP_KEY] as (() => void)[] | undefined;\n if (!cleanups) return;\n for (const cleanup of cleanups) {\n try {\n cleanup();\n } catch (e: unknown) {\n logError(\"Plugin cleanup failed\", e);\n }\n }\n delete instance.ui[CLEANUP_KEY];\n delete instance.ui[LOADED_KEY];\n}\n\n/**\n * Get the list of loaded plugins for the given instance.\n * Useful for debugging and introspection.\n */\nexport function getLoadedPlugins(instance: SpringInstance): ReadonlyArray<LoadedPluginInfo> {\n return (instance.ui[LOADED_KEY] as LoadedPluginInfo[]) ?? [];\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,110 @@
1
+ import {
2
+ registerFallbackMigration
3
+ } from "./chunk-RUCXSQEY.js";
4
+ import {
5
+ logError
6
+ } from "./chunk-KX32MU3I.js";
7
+ import {
8
+ tryGetSpringInstance
9
+ } from "./chunk-EFUBAQCV.js";
10
+
11
+ // src/auth/auth-handler.ts
12
+ var fallbackLogoutHandler = null;
13
+ var fallbackAuthenticated = false;
14
+ var fallbackLogoutInFlight = null;
15
+ registerFallbackMigration((instance) => {
16
+ if (fallbackLogoutHandler) {
17
+ instance.core.logoutHandler = fallbackLogoutHandler;
18
+ }
19
+ if (fallbackAuthenticated) {
20
+ instance.core.authenticated = fallbackAuthenticated;
21
+ }
22
+ if (fallbackLogoutInFlight) {
23
+ const migratingPromise = fallbackLogoutInFlight;
24
+ instance.core.logoutInFlight = migratingPromise;
25
+ void migratingPromise.finally(() => {
26
+ if (instance.core.logoutInFlight === migratingPromise) {
27
+ instance.core.logoutInFlight = null;
28
+ }
29
+ });
30
+ }
31
+ });
32
+ function setLogoutHandler(handler) {
33
+ const instance = tryGetSpringInstance();
34
+ if (instance) {
35
+ instance.core.logoutHandler = handler;
36
+ } else {
37
+ fallbackLogoutHandler = handler;
38
+ }
39
+ }
40
+ function setAuthenticated(value) {
41
+ const instance = tryGetSpringInstance();
42
+ if (instance) {
43
+ instance.core.authenticated = value;
44
+ } else {
45
+ fallbackAuthenticated = value;
46
+ }
47
+ }
48
+ function isAuthenticated() {
49
+ const instance = tryGetSpringInstance();
50
+ return instance ? instance.core.authenticated : fallbackAuthenticated;
51
+ }
52
+ async function triggerLogout(reason) {
53
+ const instance = tryGetSpringInstance();
54
+ const getLogoutHandler = () => instance ? instance.core.logoutHandler : fallbackLogoutHandler;
55
+ const getLogoutInFlight = () => instance ? instance.core.logoutInFlight : fallbackLogoutInFlight;
56
+ const setLogoutInFlight = (p) => {
57
+ if (instance) {
58
+ instance.core.logoutInFlight = p;
59
+ } else {
60
+ fallbackLogoutInFlight = p;
61
+ }
62
+ };
63
+ const setAuth = (v) => {
64
+ if (instance) {
65
+ instance.core.authenticated = v;
66
+ } else {
67
+ fallbackAuthenticated = v;
68
+ }
69
+ };
70
+ const handler = getLogoutHandler();
71
+ if (!handler) return;
72
+ if (reason === "unauthorized" || reason === "token_missing") {
73
+ setAuth(false);
74
+ }
75
+ const inFlight = getLogoutInFlight();
76
+ if (inFlight) {
77
+ return inFlight;
78
+ }
79
+ const promise = (async () => {
80
+ try {
81
+ await handler(reason);
82
+ } catch (e) {
83
+ logError("Auth.logout", e);
84
+ } finally {
85
+ setLogoutInFlight(null);
86
+ }
87
+ })();
88
+ setLogoutInFlight(promise);
89
+ await promise;
90
+ }
91
+ function clearAuthState() {
92
+ const instance = tryGetSpringInstance();
93
+ if (instance) {
94
+ instance.core.logoutHandler = null;
95
+ instance.core.authenticated = false;
96
+ instance.core.logoutInFlight = null;
97
+ }
98
+ fallbackLogoutHandler = null;
99
+ fallbackAuthenticated = false;
100
+ fallbackLogoutInFlight = null;
101
+ }
102
+
103
+ export {
104
+ setLogoutHandler,
105
+ setAuthenticated,
106
+ isAuthenticated,
107
+ triggerLogout,
108
+ clearAuthState
109
+ };
110
+ //# sourceMappingURL=chunk-HFELOXDQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/auth-handler.ts"],"sourcesContent":["import { tryGetSpringInstance } from \"../instance/current-instance\";\nimport { registerFallbackMigration } from \"../instance/fallback-bridge\";\nimport type { LogoutHandler,LogoutReason } from \"../instance/spring-instance\";\nimport { logError } from \"../logger/logger\";\n\nexport type { LogoutHandler,LogoutReason };\n\n// Fallback state for code that runs before SpringInstance is created\nlet fallbackLogoutHandler: LogoutHandler | null = null;\nlet fallbackAuthenticated = false;\nlet fallbackLogoutInFlight: Promise<void> | null = null;\n\n// Migrate fallback auth state to SpringInstance when SpringProvider mounts\nregisterFallbackMigration((instance) => {\n if (fallbackLogoutHandler) {\n instance.core.logoutHandler = fallbackLogoutHandler;\n }\n if (fallbackAuthenticated) {\n instance.core.authenticated = fallbackAuthenticated;\n }\n if (fallbackLogoutInFlight) {\n const migratingPromise = fallbackLogoutInFlight;\n instance.core.logoutInFlight = migratingPromise;\n void migratingPromise.finally(() => {\n if (instance.core.logoutInFlight === migratingPromise) {\n instance.core.logoutInFlight = null;\n }\n });\n }\n});\n\nexport function setLogoutHandler(handler: LogoutHandler): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.core.logoutHandler = handler;\n } else {\n fallbackLogoutHandler = handler;\n }\n}\n\nexport function setAuthenticated(value: boolean): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.core.authenticated = value;\n } else {\n fallbackAuthenticated = value;\n }\n}\n\nexport function isAuthenticated(): boolean {\n const instance = tryGetSpringInstance();\n return instance ? instance.core.authenticated : fallbackAuthenticated;\n}\n\nexport async function triggerLogout(reason: LogoutReason): Promise<void> {\n const instance = tryGetSpringInstance();\n\n const getLogoutHandler = () => instance ? instance.core.logoutHandler : fallbackLogoutHandler;\n const getLogoutInFlight = () => instance ? instance.core.logoutInFlight : fallbackLogoutInFlight;\n const setLogoutInFlight = (p: Promise<void> | null) => {\n if (instance) {\n instance.core.logoutInFlight = p;\n } else {\n fallbackLogoutInFlight = p;\n }\n };\n const setAuth = (v: boolean) => {\n if (instance) {\n instance.core.authenticated = v;\n } else {\n fallbackAuthenticated = v;\n }\n };\n\n const handler = getLogoutHandler();\n if (!handler) return;\n if (reason === \"unauthorized\" || reason === \"token_missing\") {\n setAuth(false);\n }\n const inFlight = getLogoutInFlight();\n if (inFlight) {\n return inFlight;\n }\n const promise = (async () => {\n try {\n await handler(reason);\n } catch (e: unknown) {\n logError(\"Auth.logout\", e);\n } finally {\n setLogoutInFlight(null);\n }\n })();\n setLogoutInFlight(promise);\n await promise;\n}\n\n/**\n * Clears auth-related fallback and instance state.\n * Intended for test/integration teardown.\n */\nexport function clearAuthState(): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.core.logoutHandler = null;\n instance.core.authenticated = false;\n instance.core.logoutInFlight = null;\n }\n\n fallbackLogoutHandler = null;\n fallbackAuthenticated = false;\n fallbackLogoutInFlight = null;\n}\n"],"mappings":";;;;;;;;;;;AAQA,IAAI,wBAA8C;AAClD,IAAI,wBAAwB;AAC5B,IAAI,yBAA+C;AAGnD,0BAA0B,CAAC,aAAa;AACpC,MAAI,uBAAuB;AACvB,aAAS,KAAK,gBAAgB;AAAA,EAClC;AACA,MAAI,uBAAuB;AACvB,aAAS,KAAK,gBAAgB;AAAA,EAClC;AACA,MAAI,wBAAwB;AACxB,UAAM,mBAAmB;AACzB,aAAS,KAAK,iBAAiB;AAC/B,SAAK,iBAAiB,QAAQ,MAAM;AAChC,UAAI,SAAS,KAAK,mBAAmB,kBAAkB;AACnD,iBAAS,KAAK,iBAAiB;AAAA,MACnC;AAAA,IACJ,CAAC;AAAA,EACL;AACJ,CAAC;AAEM,SAAS,iBAAiB,SAA8B;AAC3D,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,aAAS,KAAK,gBAAgB;AAAA,EAClC,OAAO;AACH,4BAAwB;AAAA,EAC5B;AACJ;AAEO,SAAS,iBAAiB,OAAsB;AACnD,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,aAAS,KAAK,gBAAgB;AAAA,EAClC,OAAO;AACH,4BAAwB;AAAA,EAC5B;AACJ;AAEO,SAAS,kBAA2B;AACvC,QAAM,WAAW,qBAAqB;AACtC,SAAO,WAAW,SAAS,KAAK,gBAAgB;AACpD;AAEA,eAAsB,cAAc,QAAqC;AACrE,QAAM,WAAW,qBAAqB;AAEtC,QAAM,mBAAmB,MAAM,WAAW,SAAS,KAAK,gBAAgB;AACxE,QAAM,oBAAoB,MAAM,WAAW,SAAS,KAAK,iBAAiB;AAC1E,QAAM,oBAAoB,CAAC,MAA4B;AACnD,QAAI,UAAU;AACV,eAAS,KAAK,iBAAiB;AAAA,IACnC,OAAO;AACH,+BAAyB;AAAA,IAC7B;AAAA,EACJ;AACA,QAAM,UAAU,CAAC,MAAe;AAC5B,QAAI,UAAU;AACV,eAAS,KAAK,gBAAgB;AAAA,IAClC,OAAO;AACH,8BAAwB;AAAA,IAC5B;AAAA,EACJ;AAEA,QAAM,UAAU,iBAAiB;AACjC,MAAI,CAAC,QAAS;AACd,MAAI,WAAW,kBAAkB,WAAW,iBAAiB;AACzD,YAAQ,KAAK;AAAA,EACjB;AACA,QAAM,WAAW,kBAAkB;AACnC,MAAI,UAAU;AACV,WAAO;AAAA,EACX;AACA,QAAM,WAAW,YAAY;AACzB,QAAI;AACA,YAAM,QAAQ,MAAM;AAAA,IACxB,SAAS,GAAY;AACjB,eAAS,eAAe,CAAC;AAAA,IAC7B,UAAE;AACE,wBAAkB,IAAI;AAAA,IAC1B;AAAA,EACJ,GAAG;AACH,oBAAkB,OAAO;AACzB,QAAM;AACV;AAMO,SAAS,iBAAuB;AACnC,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,aAAS,KAAK,gBAAgB;AAC9B,aAAS,KAAK,gBAAgB;AAC9B,aAAS,KAAK,iBAAiB;AAAA,EACnC;AAEA,0BAAwB;AACxB,0BAAwB;AACxB,2BAAyB;AAC7B;","names":[]}