@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,146 @@
1
+ import {
2
+ getTelemetryAdapter
3
+ } from "./chunk-S6RPCN5H.js";
4
+ import {
5
+ registerFallbackMigration
6
+ } from "./chunk-RUCXSQEY.js";
7
+ import {
8
+ logError
9
+ } from "./chunk-KX32MU3I.js";
10
+ import {
11
+ devThrow,
12
+ devWarn,
13
+ tryGetSpringInstance
14
+ } from "./chunk-EFUBAQCV.js";
15
+
16
+ // src/events/event-bus.ts
17
+ function getListeners() {
18
+ const instance = tryGetSpringInstance();
19
+ if (instance) return instance.core.eventListeners;
20
+ return fallbackListeners;
21
+ }
22
+ var fallbackListeners = /* @__PURE__ */ new Map();
23
+ registerFallbackMigration((instance) => {
24
+ if (fallbackListeners.size === 0) return;
25
+ const target = instance.core.eventListeners;
26
+ for (const [event, entries] of fallbackListeners) {
27
+ if (entries.length === 0) continue;
28
+ const existing = target.get(event);
29
+ if (!existing) {
30
+ target.set(event, [...entries]);
31
+ } else {
32
+ for (const entry of entries) {
33
+ if (!existing.includes(entry)) {
34
+ existing.push(entry);
35
+ }
36
+ }
37
+ }
38
+ }
39
+ });
40
+ var LEAK_THRESHOLD = 100;
41
+ function removeHandlerEntry(listeners, event, entry) {
42
+ const entries = listeners.get(event);
43
+ if (!entries) return;
44
+ const idx = entries.indexOf(entry);
45
+ if (idx !== -1) entries.splice(idx, 1);
46
+ }
47
+ function addHandler(event, handler, once2) {
48
+ if (typeof handler !== "function") {
49
+ devThrow("EventBus", `on("${event}"): handler must be a function`);
50
+ return () => {
51
+ };
52
+ }
53
+ const listeners = getListeners();
54
+ let entries = listeners.get(event);
55
+ if (!entries) {
56
+ entries = [];
57
+ listeners.set(event, entries);
58
+ }
59
+ if (entries.length >= LEAK_THRESHOLD) {
60
+ devWarn("EventBus", `Event "${event}" has ${entries.length}+ listeners \u2014 possible memory leak`);
61
+ }
62
+ const entry = { handler, once: once2 };
63
+ entries.push(entry);
64
+ return () => {
65
+ removeHandlerEntry(listeners, event, entry);
66
+ if (listeners !== fallbackListeners) {
67
+ removeHandlerEntry(fallbackListeners, event, entry);
68
+ }
69
+ const instance = tryGetSpringInstance();
70
+ if (instance) {
71
+ removeHandlerEntry(instance.core.eventListeners, event, entry);
72
+ }
73
+ };
74
+ }
75
+ function on(event, handler) {
76
+ return addHandler(event, handler, false);
77
+ }
78
+ function once(event, handler) {
79
+ return addHandler(event, handler, true);
80
+ }
81
+ function emit(event, ...[payload]) {
82
+ const listeners = getListeners();
83
+ const entries = listeners.get(event);
84
+ if (!entries || entries.length === 0) return;
85
+ const snapshot = entries.slice();
86
+ for (let i = 0; i < snapshot.length; i++) {
87
+ const entry = snapshot[i];
88
+ if (!entry) continue;
89
+ try {
90
+ const result = entry.handler(payload);
91
+ if (result && typeof result.catch === "function") {
92
+ result.catch((err) => {
93
+ logError(`EventBus handler #${i} for "${event}" threw`, err);
94
+ getTelemetryAdapter().trackError(err, { source: "EventBus", event });
95
+ });
96
+ }
97
+ } catch (err) {
98
+ logError(`EventBus handler #${i} for "${event}" threw`, err);
99
+ getTelemetryAdapter().trackError(err, { source: "EventBus", event });
100
+ }
101
+ if (entry.once) {
102
+ const liveIdx = entries.indexOf(entry);
103
+ if (liveIdx !== -1) entries.splice(liveIdx, 1);
104
+ }
105
+ }
106
+ }
107
+ async function emitAsync(event, ...[payload]) {
108
+ const listeners = getListeners();
109
+ const entries = listeners.get(event);
110
+ if (!entries || entries.length === 0) return;
111
+ const snapshot = entries.slice();
112
+ for (let i = 0; i < snapshot.length; i++) {
113
+ const entry = snapshot[i];
114
+ if (!entry) continue;
115
+ try {
116
+ await entry.handler(payload);
117
+ } catch (err) {
118
+ logError(`EventBus async handler #${i} for "${event}" threw`, err);
119
+ getTelemetryAdapter().trackError(err, { source: "EventBus", event });
120
+ }
121
+ if (entry.once) {
122
+ const liveIdx = entries.indexOf(entry);
123
+ if (liveIdx !== -1) entries.splice(liveIdx, 1);
124
+ }
125
+ }
126
+ }
127
+ function clear() {
128
+ const listeners = getListeners();
129
+ listeners.clear();
130
+ if (listeners !== fallbackListeners) {
131
+ fallbackListeners.clear();
132
+ }
133
+ }
134
+ function clearAll() {
135
+ const instance = tryGetSpringInstance();
136
+ if (instance) {
137
+ instance.core.eventListeners.clear();
138
+ }
139
+ fallbackListeners.clear();
140
+ }
141
+ var eventBus = { on, once, emit, emitAsync, clear, clearAll };
142
+
143
+ export {
144
+ eventBus
145
+ };
146
+ //# sourceMappingURL=chunk-OSSX443T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/events/event-bus.ts"],"sourcesContent":["/**\n * Typed event bus for framework-wide communication.\n * Handlers that throw are caught and logged — they never block other handlers.\n * @module event-bus\n */\n\nimport { getTelemetryAdapter } from \"../adapters/telemetry-adapter\";\nimport { tryGetSpringInstance } from \"../instance/current-instance\";\nimport { registerFallbackMigration } from \"../instance/fallback-bridge\";\nimport type { HandlerEntry } from \"../instance/spring-instance\";\nimport { logError } from \"../logger/logger\";\nimport { devThrow, devWarn } from \"../utils/dev-warnings\";\nimport type { FrameworkEventMap } from \"./event-types\";\n\ntype EventHandler<T> = (payload: T) => void | Promise<void>;\n\nfunction getListeners(): Map<string, HandlerEntry[]> {\n const instance = tryGetSpringInstance();\n if (instance) return instance.core.eventListeners;\n // Fallback for code that runs before SpringProvider mounts\n return fallbackListeners;\n}\n\nconst fallbackListeners = new Map<string, HandlerEntry[]>();\n\n// Migrate fallback event listeners to SpringInstance when SpringProvider mounts.\n// Listeners added before the instance exists would otherwise be silently lost.\nregisterFallbackMigration((instance) => {\n if (fallbackListeners.size === 0) return;\n\n const target = instance.core.eventListeners;\n for (const [event, entries] of fallbackListeners) {\n if (entries.length === 0) continue;\n const existing = target.get(event);\n if (!existing) {\n // No listeners on the instance yet for this event — move them all\n target.set(event, [...entries]);\n } else {\n // Merge: only add entries not already present on the target\n for (const entry of entries) {\n if (!existing.includes(entry)) {\n existing.push(entry);\n }\n }\n }\n }\n});\n\nconst LEAK_THRESHOLD = 100;\n\nfunction removeHandlerEntry(listeners: Map<string, HandlerEntry[]>, event: string, entry: HandlerEntry): void {\n const entries = listeners.get(event);\n if (!entries) return;\n const idx = entries.indexOf(entry);\n if (idx !== -1) entries.splice(idx, 1);\n}\n\nfunction addHandler(event: string, handler: EventHandler<unknown>, once: boolean): () => void {\n if (typeof handler !== \"function\") {\n devThrow(\"EventBus\", `on(\"${event}\"): handler must be a function`);\n return () => {};\n }\n\n const listeners = getListeners();\n let entries = listeners.get(event);\n if (!entries) {\n entries = [];\n listeners.set(event, entries);\n }\n if (entries.length >= LEAK_THRESHOLD) {\n devWarn(\"EventBus\", `Event \"${event}\" has ${entries.length}+ listeners — possible memory leak`);\n }\n\n const entry: HandlerEntry = { handler, once };\n entries.push(entry);\n\n return () => {\n removeHandlerEntry(listeners, event, entry);\n if (listeners !== fallbackListeners) {\n removeHandlerEntry(fallbackListeners, event, entry);\n }\n const instance = tryGetSpringInstance();\n if (instance) {\n removeHandlerEntry(instance.core.eventListeners, event, entry);\n }\n };\n}\n\n/**\n * Register a handler for an event. Returns an unsubscribe function.\n *\n * @param event - Event name from FrameworkEventMap (e.g. \"auth:logout\", \"form:saved\")\n * @param handler - Callback invoked with the event payload; may be sync or async\n * @returns An unsubscribe function that removes this handler.\n */\nfunction on<K extends keyof FrameworkEventMap>(event: K, handler: EventHandler<FrameworkEventMap[K]>): () => void {\n return addHandler(event as string, handler as EventHandler<unknown>, false);\n}\n\n/**\n * Register a one-time handler. Automatically removed after the first call.\n *\n * @param event - Event name from FrameworkEventMap\n * @param handler - Callback invoked once with the event payload, then auto-removed\n * @returns An unsubscribe function that removes this handler before it fires.\n */\nfunction once<K extends keyof FrameworkEventMap>(event: K, handler: EventHandler<FrameworkEventMap[K]>): () => void {\n return addHandler(event as string, handler as EventHandler<unknown>, true);\n}\n\n/**\n * Emit an event synchronously. Handlers are called in registration order.\n * Handler errors are caught and logged but do not stop other handlers.\n *\n * @param event - Event name to emit\n * @param payload - Event payload matching the type defined in FrameworkEventMap (omit for void events)\n */\nfunction emit<K extends keyof FrameworkEventMap>(\n event: K,\n ...[payload]: FrameworkEventMap[K] extends void ? [undefined?] : [FrameworkEventMap[K]]\n): void {\n const listeners = getListeners();\n const entries = listeners.get(event as string);\n if (!entries || entries.length === 0) return;\n\n // Snapshot so that unsubscribes during iteration don't skip handlers\n const snapshot = entries.slice();\n\n for (let i = 0; i < snapshot.length; i++) {\n const entry = snapshot[i];\n if (!entry) continue;\n try {\n const result = entry.handler(payload);\n // Catch rejections from async handlers passed to synchronous emit()\n if (result && typeof (result as Promise<void>).catch === \"function\") {\n (result as Promise<void>).catch((err) => {\n logError(`EventBus handler #${i} for \"${event as string}\" threw`, err);\n getTelemetryAdapter().trackError(err, { source: \"EventBus\", event: event as string });\n });\n }\n } catch (err) {\n logError(`EventBus handler #${i} for \"${event as string}\" threw`, err);\n getTelemetryAdapter().trackError(err, { source: \"EventBus\", event: event as string });\n }\n if (entry.once) {\n const liveIdx = entries.indexOf(entry);\n if (liveIdx !== -1) entries.splice(liveIdx, 1);\n }\n }\n}\n\n/**\n * Emit an event and await all handlers (including async ones).\n * Handler errors are caught and logged but do not stop other handlers.\n *\n * @param event - Event name to emit\n * @param payload - Event payload matching the type defined in FrameworkEventMap (omit for void events)\n * @returns Resolves when all handlers have completed.\n */\nasync function emitAsync<K extends keyof FrameworkEventMap>(\n event: K,\n ...[payload]: FrameworkEventMap[K] extends void ? [undefined?] : [FrameworkEventMap[K]]\n): Promise<void> {\n const listeners = getListeners();\n const entries = listeners.get(event as string);\n if (!entries || entries.length === 0) return;\n\n // Snapshot so that unsubscribes during iteration don't skip handlers\n const snapshot = entries.slice();\n\n for (let i = 0; i < snapshot.length; i++) {\n const entry = snapshot[i];\n if (!entry) continue;\n try {\n await entry.handler(payload);\n } catch (err) {\n logError(`EventBus async handler #${i} for \"${event as string}\" threw`, err);\n getTelemetryAdapter().trackError(err, { source: \"EventBus\", event: event as string });\n }\n if (entry.once) {\n const liveIdx = entries.indexOf(entry);\n if (liveIdx !== -1) entries.splice(liveIdx, 1);\n }\n }\n}\n\n/**\n * Remove all handlers for all events on both listener maps\n * (active instance map and fallback map).\n */\nfunction clear(): void {\n const listeners = getListeners();\n listeners.clear();\n if (listeners !== fallbackListeners) {\n fallbackListeners.clear();\n }\n}\n\n/**\n * Remove all event listeners from both the SpringInstance (if active)\n * and the fallback listener map. Useful for full teardown in tests\n * or when disposing the entire framework.\n */\nfunction clearAll(): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.core.eventListeners.clear();\n }\n fallbackListeners.clear();\n}\n\n/** Singleton event bus facade — delegates to the current SpringInstance's event listeners */\nexport const eventBus = { on, once, emit, emitAsync, clear, clearAll };\n"],"mappings":"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","names":["once"]}
@@ -0,0 +1,78 @@
1
+ // src/errors/errors.ts
2
+ var SpringError = class extends Error {
3
+ constructor(message, code) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = "SpringError";
7
+ Object.setPrototypeOf(this, new.target.prototype);
8
+ }
9
+ };
10
+ var SpringConfigError = class extends SpringError {
11
+ constructor(message) {
12
+ super(message, "CONFIG_ERROR");
13
+ this.name = "SpringConfigError";
14
+ Object.setPrototypeOf(this, new.target.prototype);
15
+ }
16
+ };
17
+ var SpringValidationError = class extends SpringError {
18
+ constructor(message, field) {
19
+ super(message, "VALIDATION_ERROR");
20
+ this.field = field;
21
+ this.name = "SpringValidationError";
22
+ Object.setPrototypeOf(this, new.target.prototype);
23
+ }
24
+ };
25
+ var SpringSlotError = class extends SpringError {
26
+ slotName;
27
+ constructor(slotName, message) {
28
+ super(`Slot "${slotName}": ${message}`, "SLOT_ERROR");
29
+ this.name = "SpringSlotError";
30
+ this.slotName = slotName;
31
+ Object.setPrototypeOf(this, new.target.prototype);
32
+ }
33
+ };
34
+ var SpringApiError = class extends SpringError {
35
+ constructor(message, statusCode, endpoint, code = "API_ERROR") {
36
+ super(message, code);
37
+ this.statusCode = statusCode;
38
+ this.endpoint = endpoint;
39
+ this.name = "SpringApiError";
40
+ Object.setPrototypeOf(this, new.target.prototype);
41
+ }
42
+ };
43
+ var SpringPluginError = class extends SpringError {
44
+ constructor(message, pluginName, code = "PLUGIN_ERROR") {
45
+ super(message, code);
46
+ this.pluginName = pluginName;
47
+ this.name = "SpringPluginError";
48
+ Object.setPrototypeOf(this, new.target.prototype);
49
+ }
50
+ };
51
+ var SpringAdapterError = class extends SpringError {
52
+ constructor(message, adapterName, code = "ADAPTER_ERROR") {
53
+ super(message, code);
54
+ this.adapterName = adapterName;
55
+ this.name = "SpringAdapterError";
56
+ Object.setPrototypeOf(this, new.target.prototype);
57
+ }
58
+ };
59
+ var SpringMiddlewareError = class extends SpringError {
60
+ constructor(message, slot, code = "MIDDLEWARE_ERROR") {
61
+ super(message, code);
62
+ this.slot = slot;
63
+ this.name = "SpringMiddlewareError";
64
+ Object.setPrototypeOf(this, new.target.prototype);
65
+ }
66
+ };
67
+
68
+ export {
69
+ SpringError,
70
+ SpringConfigError,
71
+ SpringValidationError,
72
+ SpringSlotError,
73
+ SpringApiError,
74
+ SpringPluginError,
75
+ SpringAdapterError,
76
+ SpringMiddlewareError
77
+ };
78
+ //# sourceMappingURL=chunk-PT4DIYUK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors/errors.ts"],"sourcesContent":["/**\n * Custom error classes for the SPRING framework.\n * Provides structured, typed errors for better error handling by consumers.\n * @module errors\n */\n\nexport class SpringError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = \"SpringError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SpringConfigError extends SpringError {\n constructor(message: string) {\n super(message, \"CONFIG_ERROR\");\n this.name = \"SpringConfigError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SpringValidationError extends SpringError {\n constructor(\n message: string,\n public readonly field?: string,\n ) {\n super(message, \"VALIDATION_ERROR\");\n this.name = \"SpringValidationError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SpringSlotError extends SpringError {\n public readonly slotName: string;\n\n constructor(slotName: string, message: string) {\n super(`Slot \"${slotName}\": ${message}`, \"SLOT_ERROR\");\n this.name = \"SpringSlotError\";\n this.slotName = slotName;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SpringApiError extends SpringError {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly endpoint: string,\n code = \"API_ERROR\" as const,\n ) {\n super(message, code);\n this.name = \"SpringApiError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SpringPluginError extends SpringError {\n constructor(\n message: string,\n public readonly pluginName: string,\n code = \"PLUGIN_ERROR\" as const,\n ) {\n super(message, code);\n this.name = \"SpringPluginError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SpringAdapterError extends SpringError {\n constructor(\n message: string,\n public readonly adapterName: string,\n code = \"ADAPTER_ERROR\" as const,\n ) {\n super(message, code);\n this.name = \"SpringAdapterError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SpringMiddlewareError extends SpringError {\n constructor(\n message: string,\n public readonly slot: string,\n code = \"MIDDLEWARE_ERROR\" as const,\n ) {\n super(message, code);\n this.name = \"SpringMiddlewareError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n"],"mappings":";AAMO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACnC,YACI,SACgB,MAClB;AACE,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EACpD;AACJ;AAEO,IAAM,oBAAN,cAAgC,YAAY;AAAA,EAC/C,YAAY,SAAiB;AACzB,UAAM,SAAS,cAAc;AAC7B,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EACpD;AACJ;AAEO,IAAM,wBAAN,cAAoC,YAAY;AAAA,EACnD,YACI,SACgB,OAClB;AACE,UAAM,SAAS,kBAAkB;AAFjB;AAGhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EACpD;AACJ;AAEO,IAAM,kBAAN,cAA8B,YAAY;AAAA,EAC7B;AAAA,EAEhB,YAAY,UAAkB,SAAiB;AAC3C,UAAM,SAAS,QAAQ,MAAM,OAAO,IAAI,YAAY;AACpD,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EACpD;AACJ;AAEO,IAAM,iBAAN,cAA6B,YAAY;AAAA,EAC5C,YACI,SACgB,YACA,UAChB,OAAO,aACT;AACE,UAAM,SAAS,IAAI;AAJH;AACA;AAIhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EACpD;AACJ;AAEO,IAAM,oBAAN,cAAgC,YAAY;AAAA,EAC/C,YACI,SACgB,YAChB,OAAO,gBACT;AACE,UAAM,SAAS,IAAI;AAHH;AAIhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EACpD;AACJ;AAEO,IAAM,qBAAN,cAAiC,YAAY;AAAA,EAChD,YACI,SACgB,aAChB,OAAO,iBACT;AACE,UAAM,SAAS,IAAI;AAHH;AAIhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EACpD;AACJ;AAEO,IAAM,wBAAN,cAAoC,YAAY;AAAA,EACnD,YACI,SACgB,MAChB,OAAO,oBACT;AACE,UAAM,SAAS,IAAI;AAHH;AAIhB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EACpD;AACJ;","names":[]}
@@ -0,0 +1,51 @@
1
+ // src/types/form-types.ts
2
+ function getIdentifiableId(obj, key) {
3
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj) || obj instanceof Date) return void 0;
4
+ const o = obj;
5
+ if (key) {
6
+ const val = o[key];
7
+ if (val !== void 0 && val !== null && (typeof val === "string" || typeof val === "number")) return val;
8
+ }
9
+ if (o.id_public !== void 0 && o.id_public !== null) return o.id_public;
10
+ if (o.id !== void 0 && o.id !== null) return o.id;
11
+ return void 0;
12
+ }
13
+ function storeBoundary(value) {
14
+ return value;
15
+ }
16
+ function toFormDataValue(value) {
17
+ return value;
18
+ }
19
+ function isFormFieldRuntime(val) {
20
+ return val !== null && typeof val === "object" && !Array.isArray(val) && !(val instanceof Date) && "value" in val && "title" in val;
21
+ }
22
+ function getField(state, key) {
23
+ if (!state) return void 0;
24
+ const val = state[key];
25
+ if (val && typeof val === "object" && !Array.isArray(val) && !(val instanceof Date)) {
26
+ return val;
27
+ }
28
+ return void 0;
29
+ }
30
+ function getSubState(state, key) {
31
+ if (!state) return void 0;
32
+ const val = state[key];
33
+ if (val && typeof val === "object" && !Array.isArray(val) && !(val instanceof Date)) {
34
+ return val;
35
+ }
36
+ return void 0;
37
+ }
38
+ function getStoreValue(state, key) {
39
+ return state[key];
40
+ }
41
+
42
+ export {
43
+ getIdentifiableId,
44
+ storeBoundary,
45
+ toFormDataValue,
46
+ isFormFieldRuntime,
47
+ getField,
48
+ getSubState,
49
+ getStoreValue
50
+ };
51
+ //# sourceMappingURL=chunk-QAVWXARR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/form-types.ts"],"sourcesContent":["/**\n * Types for FormManager - runtime shapes of fields, details, and store API.\n * @module form-types\n */\n\nimport type { Permission } from \"./grid-types\";\n\n/** Concrete union type for form field values (JSON-like data from API) */\nexport type FormDataValue =\n | string\n | number\n | boolean\n | Date\n | null\n | undefined\n | FormDataValue[]\n | { [key: string]: FormDataValue };\n\n/**\n * Form field value — at runtime includes: string, number, boolean, Date, null, undefined,\n * arrays, plain objects (JSON data from API). TS interface limitation: cannot be expressed as a union\n * without losing compatibility with TS interfaces (they lack implicit index signatures).\n */\nexport type FormFieldValue = unknown;\n\n/** Zustand store state — top-level keys (detail, list, ...) */\nexport interface FormStoreState {\n [key: string]: FormFieldValue;\n}\n\n/** Indexable object with FormDataValue entries — concrete type for dynamic field access in utilities */\nexport interface IndexedFormData {\n [key: string]: FormDataValue;\n}\n\n/** Indexable object — concrete type for generic object traversal (deepCopy, tree access) */\nexport interface Indexable {\n [key: string]: FormDataValue;\n}\n\n/** Plain object of a collection item — with known meta keys */\nexport interface FormCollectionItem {\n __cid?: string;\n id_public?: string;\n [key: string]: FormDataValue;\n}\n\n/** Map of form fields — fieldName -> FormFieldRuntime */\nexport interface FormDetailFields {\n [key: string]: FormFieldRuntime;\n}\n\n/** Structure definition for a nested object/collection */\nexport interface FormStructureDef {\n [key: string]: FormFieldDefinitionRuntime;\n}\n\n/** Forward declaration for FormManagerImpl - avoids circular dependency */\nexport interface FormManagerRef {\n handleForm: <_T>(\n setState: (updater: (state: FormStoreState) => FormStoreState) => void,\n collectionKeys?: string[] | string,\n isFilter?: boolean,\n ) => FormCollectionActions;\n}\n\n/** Runtime shape of a field definition in structure (metadata without value) */\nexport interface FormFieldDefinitionRuntime {\n value?: FormDataValue;\n title?: string | ((item: FormCollectionItem) => string);\n resourceKey?: string;\n validation?: string | ((value: FormDataValue, structure: FormDetailFields) => string);\n required?: (structure: FormDetailFields) => boolean;\n skipValidation?: boolean;\n skipField?: boolean;\n help?: string;\n dataSource?: FormDataValue[];\n visible?: (structure: FormDetailFields) => boolean;\n disabled?: (structure: FormDetailFields) => boolean;\n onChange?: (value: FormDataValue, formManager: FormManagerRef) => void;\n [key: string]: FormFieldValue;\n}\n\n/** Core field identity and value. */\nexport interface FormFieldCore {\n value?: FormDataValue;\n items?: FormCollectionItem[];\n structure?: FormStructureDef;\n title?: string | ((item: FormCollectionItem) => string);\n resourceKey?: string;\n key?: string;\n help?: string;\n dataSource?: FormDataValue[];\n}\n\n/** Validation facet of a form field. */\nexport interface FormFieldValidation {\n validation?: string | ((value: FormDataValue, structure: FormDetailFields) => string);\n required?: (structure: FormDetailFields) => boolean;\n skipValidation?: boolean;\n skipField?: boolean;\n serverError?: string;\n forceDefault?: boolean;\n}\n\n/** Mutation methods for form fields. */\nexport interface FormFieldMutation {\n set?: (newValue: FormDataValue, index?: number, fieldName?: string) => void;\n add?: (parent: FormCollectionItem | null, defaultValue?: FormCollectionItem) => void;\n remove?: (item: FormCollectionItem) => void;\n}\n\n/** Lifecycle hooks for form fields (collection items, change events). */\nexport interface FormFieldLifecycle {\n onChange?: (value: FormDataValue, formManager: FormManagerRef) => void;\n onBeforeAdd?: (newItem: FormCollectionItem) => FormCollectionItem | undefined;\n onAfterAdd?: (addedItem: FormCollectionItem, allItems: FormCollectionItem[]) => void;\n onBeforeRemove?: (item: FormCollectionItem) => boolean | void;\n onAfterRemove?: (removedItem: FormCollectionItem) => void;\n}\n\n/** Visibility facet of a form field. */\nexport interface FormFieldVisibility {\n visible?: (structure: FormDetailFields) => boolean;\n disabled?: (structure: FormDetailFields) => boolean;\n}\n\n/** Runtime shape of a single form field — intersection of all facets. 100% backward-compatible. */\nexport interface FormFieldRuntime\n extends FormFieldCore, FormFieldValidation, FormFieldMutation, FormFieldLifecycle, FormFieldVisibility {\n [key: string]: FormFieldValue;\n}\n\nexport type FormChangedPath = string;\n\nexport interface FormChangesSnapshot {\n isChanged: boolean;\n changedPaths: FormChangedPath[];\n lastChangedPath?: FormChangedPath;\n}\n\nexport interface FormChangesApi {\n get(): FormChangesSnapshot;\n isChanged(path: FormChangedPath): boolean;\n markClean(): void;\n touch(path: FormChangedPath, previousValue: FormDataValue, currentValue: FormDataValue): void;\n setChanged(path: FormChangedPath, changed: boolean): void;\n exportBaseline?: () => FormStoreState;\n importBaseline?: (baseline: FormStoreState) => void;\n}\n\n/** Runtime state of the entire form detail - access to fields via string index, specific runtime keys */\nexport interface FormDetailRuntime extends FormStoreState {\n isEdited?: boolean;\n isValidated?: boolean;\n changes?: FormChangesApi;\n formManager?: FormManagerRef;\n form?: (collectionKeys?: string | string[]) => FormCollectionActions;\n}\n\n/** Minimal interface for createForm return value — Zustand store instance. */\nexport interface FormStoreInstance {\n getState: () => object;\n}\n\n/** Type for the Zustand-like store used by FormManager */\nexport interface FormStoreApi {\n getState: () => FormStoreState;\n setState: (updater: (s: FormStoreState) => FormStoreState) => void;\n getInitialState: () => FormStoreState;\n}\n\n/** Actions for manipulating collections in the form (add/remove/set) */\nexport interface FormCollectionActions {\n add: (parent: FormCollectionItem | null, newItem: FormCollectionItem) => void;\n remove: (itemToRemove: FormCollectionItem) => void;\n set: (newValue: FormFieldValue, key: string, item: FormCollectionItem) => void;\n}\n\n/** Non-generic version of FormManager for contexts where invariant M is not needed. */\nexport interface FormManagerBase {\n clearFields(): void;\n getFields(): (FormStoreState & { id_public?: string }) | null;\n setFields(value: FormStoreState, opts?: { markClean?: boolean }): void;\n get(key: string): FormFieldValue;\n set(key: string, value: FormFieldValue): void;\n getReadOnly(): boolean;\n setReadOnly(value: boolean): void;\n setIdPublic(value: string): void;\n validate(): boolean;\n save(\n url: string,\n fields: FormStoreState & { id_public?: string },\n validations?: boolean,\n ): Promise<FormStoreState | false>;\n load<T extends FormStoreState>(\n route: string,\n id_public?: string,\n detailData?: T,\n copyFrom?: string,\n ): Promise<T | undefined>;\n getPermissions(): Permission | undefined;\n setPermissions(permissions: Permission | undefined): void;\n transformDs(items: FormCollectionItem[], structure?: FormStructureDef): FormCollectionItem[];\n handleForm<_T>(\n setState: (updater: (state: FormStoreState) => FormStoreState) => void,\n collectionKeys?: string[] | string,\n isFilter?: boolean,\n ): FormCollectionActions;\n}\n\n/** Minimal interface for ListManager — covers .load() called from Detail/Context. */\nexport interface ListManagerLoadable {\n load(allowLoad?: boolean): Promise<object | null | undefined | void>;\n highlightRow(id_public: string): void;\n}\n\n/** Form readOnly state — minimal interface for the readOnly field in FormPageForm */\nexport interface ReadOnlyField {\n value?: boolean;\n set?: (newValue: boolean) => void;\n}\n\nexport interface Identifiable {\n id_public?: string | number;\n id?: string | number;\n [key: string]: FormFieldValue;\n}\n\n/** Extracts ID from an object — looks for `key`, `id_public`, `id` (in that order). */\nexport function getIdentifiableId(obj: unknown, key?: string): string | number | undefined {\n if (typeof obj !== \"object\" || obj === null || Array.isArray(obj) || obj instanceof Date) return undefined;\n const o = obj as Identifiable;\n if (key) {\n const val = o[key];\n if (val !== undefined && val !== null && (typeof val === \"string\" || typeof val === \"number\")) return val;\n }\n if (o.id_public !== undefined && o.id_public !== null) return o.id_public;\n if (o.id !== undefined && o.id !== null) return o.id;\n return undefined;\n}\n\n/** Zustand boundary cast — encapsulates `as unknown as T` for Store type invariance. */\nexport function storeBoundary<T>(value: FormFieldValue): T {\n return value as T;\n}\n\n/** Boundary function: narrows FormFieldValue (=unknown) to FormDataValue at the unknown→typed boundary. */\nexport function toFormDataValue(value: FormFieldValue): FormDataValue {\n return value as FormDataValue;\n}\n\n/** Type guard: checks if an unknown value is a FormFieldRuntime-like object (has `value` key and is not a Date/Array). */\nexport function isFormFieldRuntime(val: unknown): val is FormFieldRuntime {\n return (\n val !== null &&\n typeof val === \"object\" &&\n !Array.isArray(val) &&\n !(val instanceof Date) &&\n \"value\" in val &&\n \"title\" in val\n );\n}\n\n/** Typed accessor: returns FormFieldRuntime from FormStoreState[key] with a runtime type check. */\nexport function getField(state: FormStoreState | null | undefined, key: string): FormFieldRuntime | undefined {\n if (!state) return undefined;\n const val = state[key];\n if (val && typeof val === \"object\" && !Array.isArray(val) && !(val instanceof Date)) {\n return val as FormFieldRuntime;\n }\n return undefined;\n}\n\n/** Typed accessor: returns nested FormStoreState from FormStoreState[key] with a runtime check. */\nexport function getSubState(state: FormStoreState | null | undefined, key: string): FormStoreState | undefined {\n if (!state) return undefined;\n const val = state[key];\n if (val && typeof val === \"object\" && !Array.isArray(val) && !(val instanceof Date)) {\n return val as FormStoreState;\n }\n return undefined;\n}\n\n/** Typed accessor: returns a value from Zustand store state under the given key. */\nexport function getStoreValue<T>(state: object, key: string): T {\n return (state as FormStoreState)[key] as T;\n}\n\n/** Filter value in the TreeList/List filter system — includes lookup objects */\nexport type FilterFieldValue =\n | string\n | number\n | boolean\n | Date\n | null\n | undefined\n | { id_public?: string | number; value?: string | number; [key: string]: FilterFieldValue | undefined }\n | FilterFieldValue[];\n\n/** Shape of an error object from API/Axios */\nexport interface ApiErrorLike {\n code?: string;\n name?: string;\n message?: string;\n status?: number;\n data?: unknown;\n}\n\n/** Comparison function for the validation engine */\nexport type ComparisonFunction = (a: FormDataValue, b: FormDataValue) => boolean;\n\n/** Named validator function — registered via registerValidator, resolved at validation time. */\nexport type ValidatorFn = (value: FormDataValue, structure: FormDetailFields) => string;\n\nexport interface FormLifecycle<T = FormStoreState> {\n onBeforeLoad?: (id: string) => void | Promise<void>;\n onAfterLoad?: (data: FormStoreState) => void;\n onBeforeSave?: (fields: T) => T;\n onAfterSave?: (result: FormStoreState) => void;\n}\n"],"mappings":";AAsOO,SAAS,kBAAkB,KAAc,KAA2C;AACvF,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,MAAM,QAAQ,GAAG,KAAK,eAAe,KAAM,QAAO;AACjG,QAAM,IAAI;AACV,MAAI,KAAK;AACL,UAAM,MAAM,EAAE,GAAG;AACjB,QAAI,QAAQ,UAAa,QAAQ,SAAS,OAAO,QAAQ,YAAY,OAAO,QAAQ,UAAW,QAAO;AAAA,EAC1G;AACA,MAAI,EAAE,cAAc,UAAa,EAAE,cAAc,KAAM,QAAO,EAAE;AAChE,MAAI,EAAE,OAAO,UAAa,EAAE,OAAO,KAAM,QAAO,EAAE;AAClD,SAAO;AACX;AAGO,SAAS,cAAiB,OAA0B;AACvD,SAAO;AACX;AAGO,SAAS,gBAAgB,OAAsC;AAClE,SAAO;AACX;AAGO,SAAS,mBAAmB,KAAuC;AACtE,SACI,QAAQ,QACR,OAAO,QAAQ,YACf,CAAC,MAAM,QAAQ,GAAG,KAClB,EAAE,eAAe,SACjB,WAAW,OACX,WAAW;AAEnB;AAGO,SAAS,SAAS,OAA0C,KAA2C;AAC1G,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,MAAM,GAAG;AACrB,MAAI,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,KAAK,EAAE,eAAe,OAAO;AACjF,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAGO,SAAS,YAAY,OAA0C,KAAyC;AAC3G,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,MAAM,GAAG;AACrB,MAAI,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,KAAK,EAAE,eAAe,OAAO;AACjF,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAGO,SAAS,cAAiB,OAAe,KAAgB;AAC5D,SAAQ,MAAyB,GAAG;AACxC;","names":[]}
@@ -0,0 +1,143 @@
1
+ import {
2
+ getField
3
+ } from "./chunk-QAVWXARR.js";
4
+ import {
5
+ getContentRes
6
+ } from "./chunk-NQQIVCLX.js";
7
+ import {
8
+ registerFallbackMigration
9
+ } from "./chunk-RUCXSQEY.js";
10
+ import {
11
+ logError
12
+ } from "./chunk-KX32MU3I.js";
13
+ import {
14
+ tryGetSpringInstance
15
+ } from "./chunk-EFUBAQCV.js";
16
+
17
+ // src/validation/validations.ts
18
+ var fallbackRegistry = /* @__PURE__ */ new Map();
19
+ registerFallbackMigration((instance) => {
20
+ if (fallbackRegistry.size === 0) return;
21
+ const target = instance.core.validatorRegistry;
22
+ for (const [name, validator] of fallbackRegistry) {
23
+ if (!target.has(name)) {
24
+ target.set(name, validator);
25
+ }
26
+ }
27
+ });
28
+ function getRegistry() {
29
+ const instance = tryGetSpringInstance();
30
+ return instance ? instance.core.validatorRegistry : fallbackRegistry;
31
+ }
32
+ function registerValidator(name, fn) {
33
+ getRegistry().set(name, fn);
34
+ }
35
+ function getValidator(name) {
36
+ return getRegistry().get(name);
37
+ }
38
+ function getRegisteredValidators() {
39
+ return Array.from(getRegistry().keys());
40
+ }
41
+ function clearValidators() {
42
+ const instance = tryGetSpringInstance();
43
+ if (instance) {
44
+ instance.core.validatorRegistry.clear();
45
+ }
46
+ fallbackRegistry.clear();
47
+ }
48
+ var comparisonOps = {
49
+ "===": (a, b) => a === b,
50
+ "!==": (a, b) => a !== b,
51
+ ">=": (a, b) => Number(a) >= Number(b),
52
+ "<=": (a, b) => Number(a) <= Number(b),
53
+ ">": (a, b) => Number(a) > Number(b),
54
+ "<": (a, b) => Number(a) < Number(b)
55
+ };
56
+ function findOperatorOutsideQuotes(str) {
57
+ const ops = ["===", "!==", ">=", "<=", ">", "<"];
58
+ let inQuote = null;
59
+ let escaped = false;
60
+ for (let i = 0; i < str.length; i++) {
61
+ const ch = str[i];
62
+ if (escaped) {
63
+ escaped = false;
64
+ continue;
65
+ }
66
+ if (ch === "\\") {
67
+ escaped = true;
68
+ continue;
69
+ }
70
+ if (inQuote) {
71
+ if (ch === inQuote) inQuote = null;
72
+ continue;
73
+ }
74
+ if (ch === '"' || ch === "'") {
75
+ inQuote = ch;
76
+ continue;
77
+ }
78
+ for (const op of ops) {
79
+ if (str.startsWith(op, i)) {
80
+ return { op, index: i };
81
+ }
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ function parseValue(raw) {
87
+ const trimmed = raw.trim();
88
+ if (trimmed === "undefined") return void 0;
89
+ if (trimmed === "null") return null;
90
+ if (trimmed === "true") return true;
91
+ if (trimmed === "false") return false;
92
+ if (/^".*"$/.test(trimmed) || /^'.*'$/.test(trimmed)) return trimmed.slice(1, -1);
93
+ const num = Number(trimmed);
94
+ if (!isNaN(num) && trimmed !== "") return num;
95
+ return trimmed;
96
+ }
97
+ function resolveToken(token, structure) {
98
+ const trimmed = token.trim();
99
+ const fieldMatch = trimmed.match(/^\{(\w+)\}$/);
100
+ const fieldName = fieldMatch?.[1];
101
+ if (fieldName) {
102
+ const field = getField(structure, fieldName);
103
+ return field?.value ?? void 0;
104
+ }
105
+ return parseValue(trimmed);
106
+ }
107
+ function interpretValidation(structure, validationString) {
108
+ if (!structure || !validationString) return void 0;
109
+ try {
110
+ if (/&&|\|\|/.test(validationString)) {
111
+ logError(
112
+ "Validation.interpret",
113
+ `Compound expressions are not supported: "${validationString}". Use simple comparisons.`
114
+ );
115
+ return false;
116
+ }
117
+ const opInfo = findOperatorOutsideQuotes(validationString);
118
+ if (!opInfo) {
119
+ return resolveToken(validationString, structure);
120
+ }
121
+ const { op, index: opIndex } = opInfo;
122
+ const left = resolveToken(validationString.slice(0, opIndex), structure);
123
+ const right = resolveToken(validationString.slice(opIndex + op.length), structure);
124
+ const fn = op in comparisonOps ? comparisonOps[op] : void 0;
125
+ if (!fn) {
126
+ logError("Validation.interpret", `Unknown operator "${op}" in: "${validationString}"`);
127
+ return false;
128
+ }
129
+ return fn(left, right);
130
+ } catch (error) {
131
+ logError("Validation.interpret", error);
132
+ return getContentRes("admin-validation-error");
133
+ }
134
+ }
135
+
136
+ export {
137
+ registerValidator,
138
+ getValidator,
139
+ getRegisteredValidators,
140
+ clearValidators,
141
+ interpretValidation
142
+ };
143
+ //# sourceMappingURL=chunk-RRWKDFAB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/validation/validations.ts"],"sourcesContent":["import { getContentRes } from \"../adapters/content-adapter\";\nimport { tryGetSpringInstance } from \"../instance/current-instance\";\nimport { registerFallbackMigration } from \"../instance/fallback-bridge\";\nimport { logError } from \"../logger/logger\";\nimport type { ComparisonFunction, FormDataValue, FormDetailRuntime, ValidatorFn } from \"../types/form-types\";\nimport { getField } from \"../types/form-types\";\n\nexport type { ValidatorFn } from \"../types/form-types\";\n\n// Fallback for code that runs before SpringInstance is created\nconst fallbackRegistry = new Map<string, ValidatorFn>();\n\n// Migrate pre-mount validators so they remain available after instance initialization.\nregisterFallbackMigration((instance) => {\n if (fallbackRegistry.size === 0) return;\n\n const target = instance.core.validatorRegistry;\n for (const [name, validator] of fallbackRegistry) {\n if (!target.has(name)) {\n target.set(name, validator);\n }\n }\n});\n\nfunction getRegistry(): Map<string, ValidatorFn> {\n const instance = tryGetSpringInstance();\n return instance ? instance.core.validatorRegistry : fallbackRegistry;\n}\n\n/** Register a named validator (projects can add custom validators) */\nexport function registerValidator(name: string, fn: ValidatorFn): void {\n getRegistry().set(name, fn);\n}\n\n/** Get a registered validator by name */\nexport function getValidator(name: string): ValidatorFn | undefined {\n return getRegistry().get(name);\n}\n\n/** Get names of all registered validators. Useful for debugging and introspection. */\nexport function getRegisteredValidators(): string[] {\n return Array.from(getRegistry().keys());\n}\n\n/** Remove all registered validators from both instance and fallback stores. */\nexport function clearValidators(): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.core.validatorRegistry.clear();\n }\n fallbackRegistry.clear();\n}\n\ntype ComparisonOperator = \"===\" | \"!==\" | \">=\" | \"<=\" | \">\" | \"<\";\n\nconst comparisonOps = {\n \"===\": (a, b) => a === b,\n \"!==\": (a, b) => a !== b,\n \">=\": (a, b) => Number(a) >= Number(b),\n \"<=\": (a, b) => Number(a) <= Number(b),\n \">\": (a, b) => Number(a) > Number(b),\n \"<\": (a, b) => Number(a) < Number(b),\n} satisfies Record<ComparisonOperator, ComparisonFunction>;\n\n/** Finds the first comparison operator outside quotes */\nfunction findOperatorOutsideQuotes(str: string): { op: string; index: number } | null {\n const ops = [\"===\", \"!==\", \">=\", \"<=\", \">\", \"<\"];\n let inQuote: string | null = null;\n let escaped = false;\n\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === \"\\\\\") {\n escaped = true;\n continue;\n }\n\n if (inQuote) {\n if (ch === inQuote) inQuote = null;\n continue;\n }\n\n if (ch === '\"' || ch === \"'\") {\n inQuote = ch;\n continue;\n }\n\n for (const op of ops) {\n if (str.startsWith(op, i)) {\n return { op, index: i };\n }\n }\n }\n\n return null;\n}\n\nfunction parseValue(raw: string) {\n const trimmed = raw.trim();\n if (trimmed === \"undefined\") return undefined;\n if (trimmed === \"null\") return null;\n if (trimmed === \"true\") return true;\n if (trimmed === \"false\") return false;\n if (/^\".*\"$/.test(trimmed) || /^'.*'$/.test(trimmed)) return trimmed.slice(1, -1);\n const num = Number(trimmed);\n if (!isNaN(num) && trimmed !== \"\") return num;\n return trimmed;\n}\n\n/**\n * Resolve a token in a validation expression.\n * Tokens are either field references `{fieldName}` or literal values.\n * Field values are returned directly (no string interpolation) to prevent injection.\n */\nfunction resolveToken(token: string, structure: FormDetailRuntime): FormDataValue {\n const trimmed = token.trim();\n // Field reference: {fieldName}\n const fieldMatch = trimmed.match(/^\\{(\\w+)\\}$/);\n const fieldName = fieldMatch?.[1];\n if (fieldName) {\n const field = getField(structure, fieldName);\n return field?.value ?? undefined;\n }\n return parseValue(trimmed);\n}\n\n/** Interprets dynamic validation from string — safe against injection. */\nexport function interpretValidation(structure: FormDetailRuntime, validationString: string) {\n if (!structure || !validationString) return undefined;\n try {\n if (/&&|\\|\\|/.test(validationString)) {\n logError(\n \"Validation.interpret\",\n `Compound expressions are not supported: \"${validationString}\". Use simple comparisons.`,\n );\n return false;\n }\n\n const opInfo = findOperatorOutsideQuotes(validationString);\n if (!opInfo) {\n // No operator — resolve as single token\n return resolveToken(validationString, structure);\n }\n\n const { op, index: opIndex } = opInfo;\n const left = resolveToken(validationString.slice(0, opIndex), structure);\n const right = resolveToken(validationString.slice(opIndex + op.length), structure);\n const fn = op in comparisonOps ? comparisonOps[op as ComparisonOperator] : undefined;\n if (!fn) {\n logError(\"Validation.interpret\", `Unknown operator \"${op}\" in: \"${validationString}\"`);\n return false;\n }\n return fn(left, right);\n } catch (error: unknown) {\n logError(\"Validation.interpret\", error);\n return getContentRes(\"admin-validation-error\");\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,42 @@
1
+ import {
2
+ logError
3
+ } from "./chunk-KX32MU3I.js";
4
+
5
+ // src/instance/fallback-bridge.ts
6
+ function applyAllFallbacks(instance) {
7
+ const sorted = [...fallbackMigrations.values()].sort((a, b) => {
8
+ if (b.priority !== a.priority) return b.priority - a.priority;
9
+ return a.order - b.order;
10
+ });
11
+ for (const entry of sorted) {
12
+ try {
13
+ entry.migration(instance);
14
+ } catch (e) {
15
+ logError("FallbackBridge.migration", e);
16
+ }
17
+ }
18
+ }
19
+ var fallbackMigrations = /* @__PURE__ */ new Map();
20
+ var registrationOrder = 0;
21
+ function registerFallbackMigration(migration, priority = 0) {
22
+ const existing = fallbackMigrations.get(migration);
23
+ if (existing) {
24
+ existing.priority = priority;
25
+ return;
26
+ }
27
+ fallbackMigrations.set(migration, {
28
+ migration,
29
+ priority,
30
+ order: registrationOrder++
31
+ });
32
+ }
33
+ function clearFallbackMigrations() {
34
+ fallbackMigrations.clear();
35
+ }
36
+
37
+ export {
38
+ applyAllFallbacks,
39
+ registerFallbackMigration,
40
+ clearFallbackMigrations
41
+ };
42
+ //# sourceMappingURL=chunk-RUCXSQEY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/instance/fallback-bridge.ts"],"sourcesContent":["/**\n * Centralized fallback bridge — migrates pre-mount state to a SpringInstance.\n *\n * Before SpringProvider mounts, various modules (config, adapters, event bus,\n * middleware) store state in module-level fallback variables. When SpringProvider\n * creates and sets the SpringInstance, this function migrates all fallback state\n * to the instance in a single call.\n *\n * This replaces the previous pattern where each module independently checked\n * tryGetSpringInstance() and maintained its own fallback state.\n *\n * @module fallback-bridge\n */\n\nimport { logError } from \"../logger/logger\";\nimport type { SpringInstance } from \"./spring-instance\";\n\n/**\n * Migrate all pre-mount fallback state into the given SpringInstance.\n * Called once by SpringProvider after instance creation.\n *\n * Modules register their fallback migration via registerFallbackMigration().\n * This allows packages (ui, api) to register their own migrations\n * without core needing to know about them.\n */\nexport function applyAllFallbacks(instance: SpringInstance): void {\n // Run all registered migrations from other modules in deterministic order.\n // Higher priority runs first; same priority preserves registration order.\n const sorted = [...fallbackMigrations.values()].sort((a, b) => {\n if (b.priority !== a.priority) return b.priority - a.priority;\n return a.order - b.order;\n });\n for (const entry of sorted) {\n try {\n entry.migration(instance);\n } catch (e) {\n logError(\"FallbackBridge.migration\", e);\n }\n }\n}\n\ntype FallbackMigration = (instance: SpringInstance) => void;\ninterface FallbackMigrationEntry {\n migration: FallbackMigration;\n priority: number;\n order: number;\n}\nconst fallbackMigrations = new Map<FallbackMigration, FallbackMigrationEntry>();\nlet registrationOrder = 0;\n\n/**\n * Register a fallback migration function.\n * Called by modules that maintain pre-mount fallback state (adapters, event bus, etc.)\n * to ensure their state is migrated when SpringProvider mounts.\n */\nexport function registerFallbackMigration(migration: FallbackMigration, priority = 0): void {\n const existing = fallbackMigrations.get(migration);\n if (existing) {\n existing.priority = priority;\n return;\n }\n fallbackMigrations.set(migration, {\n migration,\n priority,\n order: registrationOrder++,\n });\n}\n\n/** Clear all registered migrations. For testing only. */\nexport function clearFallbackMigrations(): void {\n fallbackMigrations.clear();\n}\n"],"mappings":";;;;;AAyBO,SAAS,kBAAkB,UAAgC;AAG9D,QAAM,SAAS,CAAC,GAAG,mBAAmB,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM;AAC3D,QAAI,EAAE,aAAa,EAAE,SAAU,QAAO,EAAE,WAAW,EAAE;AACrD,WAAO,EAAE,QAAQ,EAAE;AAAA,EACvB,CAAC;AACD,aAAW,SAAS,QAAQ;AACxB,QAAI;AACA,YAAM,UAAU,QAAQ;AAAA,IAC5B,SAAS,GAAG;AACR,eAAS,4BAA4B,CAAC;AAAA,IAC1C;AAAA,EACJ;AACJ;AAQA,IAAM,qBAAqB,oBAAI,IAA+C;AAC9E,IAAI,oBAAoB;AAOjB,SAAS,0BAA0B,WAA8B,WAAW,GAAS;AACxF,QAAM,WAAW,mBAAmB,IAAI,SAAS;AACjD,MAAI,UAAU;AACV,aAAS,WAAW;AACpB;AAAA,EACJ;AACA,qBAAmB,IAAI,WAAW;AAAA,IAC9B;AAAA,IACA;AAAA,IACA,OAAO;AAAA,EACX,CAAC;AACL;AAGO,SAAS,0BAAgC;AAC5C,qBAAmB,MAAM;AAC7B;","names":[]}
@@ -0,0 +1,64 @@
1
+ import {
2
+ registerFallbackMigration
3
+ } from "./chunk-RUCXSQEY.js";
4
+ import {
5
+ tryGetSpringInstance
6
+ } from "./chunk-EFUBAQCV.js";
7
+
8
+ // src/adapters/telemetry-adapter.ts
9
+ var noopAdapter = {
10
+ trackEvent: () => {
11
+ },
12
+ trackError: () => {
13
+ },
14
+ trackTiming: () => {
15
+ }
16
+ };
17
+ var fallbackAdapter = noopAdapter;
18
+ var UI_KEY = /* @__PURE__ */ Symbol.for("spring:telemetryAdapter");
19
+ registerFallbackMigration((instance) => {
20
+ if (fallbackAdapter !== noopAdapter) {
21
+ instance.ui[UI_KEY] = fallbackAdapter;
22
+ }
23
+ });
24
+ function setTelemetryAdapter(adapter) {
25
+ const instance = tryGetSpringInstance();
26
+ if (instance) {
27
+ instance.ui[UI_KEY] = adapter;
28
+ } else {
29
+ fallbackAdapter = adapter;
30
+ }
31
+ }
32
+ function getTelemetryAdapter() {
33
+ const instance = tryGetSpringInstance();
34
+ if (instance) {
35
+ return instance.ui[UI_KEY] ?? noopAdapter;
36
+ }
37
+ return fallbackAdapter;
38
+ }
39
+ function clearTelemetryAdapter() {
40
+ const instance = tryGetSpringInstance();
41
+ if (instance) {
42
+ delete instance.ui[UI_KEY];
43
+ }
44
+ fallbackAdapter = noopAdapter;
45
+ }
46
+ function trackEvent(name, data) {
47
+ getTelemetryAdapter().trackEvent(name, data);
48
+ }
49
+ function trackError(error, context) {
50
+ getTelemetryAdapter().trackError(error, context);
51
+ }
52
+ function trackTiming(metric, durationMs, data) {
53
+ getTelemetryAdapter().trackTiming(metric, durationMs, data);
54
+ }
55
+
56
+ export {
57
+ setTelemetryAdapter,
58
+ getTelemetryAdapter,
59
+ clearTelemetryAdapter,
60
+ trackEvent,
61
+ trackError,
62
+ trackTiming
63
+ };
64
+ //# sourceMappingURL=chunk-S6RPCN5H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/telemetry-adapter.ts"],"sourcesContent":["/**\n * Telemetry adapter — provides hooks for observability, monitoring, and analytics.\n *\n * Enterprise deployments can integrate with APM tools (DataDog, New Relic, etc.)\n * by setting a custom telemetry adapter. The framework emits telemetry events\n * at key lifecycle points (form loads, API calls, errors, etc.)\n *\n * @example\n * ```ts\n * import { setTelemetryAdapter } from \"@spring-systems/core/adapters\";\n *\n * setTelemetryAdapter({\n * trackEvent(name, data) {\n * datadogRum.addAction(name, data);\n * },\n * trackError(error, context) {\n * Sentry.captureException(error, { extra: context });\n * },\n * trackTiming(metric, durationMs, data) {\n * performance.measure(metric, { duration: durationMs });\n * },\n * });\n * ```\n *\n * @module telemetry-adapter\n */\n\nimport { tryGetSpringInstance } from \"../instance/current-instance\";\nimport { registerFallbackMigration } from \"../instance/fallback-bridge\";\n\nexport interface TelemetryAdapter {\n /**\n * Track a named event with optional structured data.\n * Called on form save, list load, login, navigation, etc.\n */\n trackEvent(name: string, data?: Record<string, unknown>): void;\n\n /**\n * Track an error occurrence with optional context.\n * Called on unhandled errors, API failures, middleware errors, etc.\n */\n trackError(error: Error | unknown, context?: Record<string, unknown>): void;\n\n /**\n * Track a timing measurement (e.g. API latency, form render time).\n * @param metric - Metric name (e.g. \"api.response_time\", \"form.load_time\")\n * @param durationMs - Duration in milliseconds\n * @param data - Optional context data\n */\n trackTiming(metric: string, durationMs: number, data?: Record<string, unknown>): void;\n}\n\nconst noopAdapter = {\n trackEvent: () => {},\n trackError: () => {},\n trackTiming: () => {},\n} satisfies TelemetryAdapter;\n\nlet fallbackAdapter: TelemetryAdapter = noopAdapter;\n\nconst UI_KEY = Symbol.for(\"spring:telemetryAdapter\");\n\n// Migrate fallback telemetry adapter to SpringInstance when SpringProvider mounts\nregisterFallbackMigration((instance) => {\n if (fallbackAdapter !== noopAdapter) {\n instance.ui[UI_KEY] = fallbackAdapter;\n }\n});\n\nexport function setTelemetryAdapter(adapter: TelemetryAdapter): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n instance.ui[UI_KEY] = adapter;\n } else {\n fallbackAdapter = adapter;\n }\n}\n\nexport function getTelemetryAdapter(): TelemetryAdapter {\n const instance = tryGetSpringInstance();\n if (instance) {\n return (instance.ui[UI_KEY] as TelemetryAdapter) ?? noopAdapter;\n }\n return fallbackAdapter;\n}\n\n/** Reset telemetry adapter to no-op defaults. For testing/integration teardown only. */\nexport function clearTelemetryAdapter(): void {\n const instance = tryGetSpringInstance();\n if (instance) {\n delete instance.ui[UI_KEY];\n }\n fallbackAdapter = noopAdapter;\n}\n\n/**\n * Convenience helper to track an event via the configured telemetry adapter.\n * Safe to call even if no adapter is configured (no-op).\n */\nexport function trackEvent(name: string, data?: Record<string, unknown>): void {\n getTelemetryAdapter().trackEvent(name, data);\n}\n\n/**\n * Convenience helper to track an error via the configured telemetry adapter.\n */\nexport function trackError(error: Error | unknown, context?: Record<string, unknown>): void {\n getTelemetryAdapter().trackError(error, context);\n}\n\n/**\n * Convenience helper to track timing via the configured telemetry adapter.\n */\nexport function trackTiming(metric: string, durationMs: number, data?: Record<string, unknown>): void {\n getTelemetryAdapter().trackTiming(metric, durationMs, data);\n}\n"],"mappings":";;;;;;;;AAoDA,IAAM,cAAc;AAAA,EAChB,YAAY,MAAM;AAAA,EAAC;AAAA,EACnB,YAAY,MAAM;AAAA,EAAC;AAAA,EACnB,aAAa,MAAM;AAAA,EAAC;AACxB;AAEA,IAAI,kBAAoC;AAExC,IAAM,SAAS,uBAAO,IAAI,yBAAyB;AAGnD,0BAA0B,CAAC,aAAa;AACpC,MAAI,oBAAoB,aAAa;AACjC,aAAS,GAAG,MAAM,IAAI;AAAA,EAC1B;AACJ,CAAC;AAEM,SAAS,oBAAoB,SAAiC;AACjE,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,aAAS,GAAG,MAAM,IAAI;AAAA,EAC1B,OAAO;AACH,sBAAkB;AAAA,EACtB;AACJ;AAEO,SAAS,sBAAwC;AACpD,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,WAAQ,SAAS,GAAG,MAAM,KAA0B;AAAA,EACxD;AACA,SAAO;AACX;AAGO,SAAS,wBAA8B;AAC1C,QAAM,WAAW,qBAAqB;AACtC,MAAI,UAAU;AACV,WAAO,SAAS,GAAG,MAAM;AAAA,EAC7B;AACA,oBAAkB;AACtB;AAMO,SAAS,WAAW,MAAc,MAAsC;AAC3E,sBAAoB,EAAE,WAAW,MAAM,IAAI;AAC/C;AAKO,SAAS,WAAW,OAAwB,SAAyC;AACxF,sBAAoB,EAAE,WAAW,OAAO,OAAO;AACnD;AAKO,SAAS,YAAY,QAAgB,YAAoB,MAAsC;AAClG,sBAAoB,EAAE,YAAY,QAAQ,YAAY,IAAI;AAC9D;","names":[]}