@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,148 @@
1
+ import { c as CapabilityMap, F as FrameworkConfigOverrides, a as FrameworkConfig } from './framework-config-types-DeUbx4bu.js';
2
+
3
+ /**
4
+ * Feature capability system for enterprise SPRING deployments.
5
+ *
6
+ * Capabilities allow framework consumers to enable/disable features
7
+ * per client deployment. Plugins can declare required capabilities
8
+ * and the framework checks them at registration time.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // In project-config.ts:
13
+ * configureCapabilities({
14
+ * charts: true,
15
+ * advancedFiltering: true,
16
+ * bulkOperations: false,
17
+ * export: { enabled: true, formats: ["xlsx", "pdf"] },
18
+ * });
19
+ *
20
+ * // In a plugin:
21
+ * if (hasCapability("advancedFiltering")) {
22
+ * registerMiddleware("list:beforeLoad", advancedFilterMiddleware);
23
+ * }
24
+ *
25
+ * // In a component:
26
+ * const canExport = hasCapability("export");
27
+ * ```
28
+ *
29
+ * @module capabilities
30
+ */
31
+
32
+ type CapabilityStore = Partial<CapabilityMap> & Record<string, unknown>;
33
+ /**
34
+ * Configure feature capabilities for this deployment.
35
+ * Merges with existing capabilities (does not replace).
36
+ */
37
+ declare function configureCapabilities(capabilities: Partial<CapabilityMap> & Record<string, unknown>): void;
38
+ /**
39
+ * Check if a capability is enabled.
40
+ * Returns false for unknown/unconfigured capabilities.
41
+ */
42
+ declare function hasCapability<K extends keyof CapabilityMap>(name: K): boolean;
43
+ /**
44
+ * Get the full capability configuration for a feature.
45
+ * Returns undefined if the capability is not configured.
46
+ */
47
+ declare function getCapability<K extends keyof CapabilityMap>(name: K): CapabilityMap[K] | undefined;
48
+ /** Get all configured capabilities. */
49
+ declare function getCapabilities(): Readonly<CapabilityStore>;
50
+ /**
51
+ * Get the names of all currently registered (configured) capabilities.
52
+ * Useful for introspection and for validating that plugin capability
53
+ * references point to actually-configured features.
54
+ */
55
+ declare function getRegisteredCapabilityNames(): string[];
56
+ /** Reset all capabilities. For testing only. */
57
+ declare function clearCapabilities(): void;
58
+
59
+ /**
60
+ * Centralized framework configuration.
61
+ * Each project sets its own values via configureFramework().
62
+ * The framework reads from getFrameworkConfig().
63
+ * @module framework-config
64
+ */
65
+
66
+ /**
67
+ * Default framework configuration. Exported as the single source of truth
68
+ * for both configureFramework() and createSpringInstance().
69
+ * Deep-frozen to prevent accidental mutation of shared defaults.
70
+ */
71
+ declare const DEFAULT_FRAMEWORK_CONFIG: FrameworkConfig;
72
+ interface ConfigValidationResult {
73
+ valid: boolean;
74
+ errors: string[];
75
+ warnings: string[];
76
+ }
77
+ /**
78
+ * Validate a framework configuration and return structured results.
79
+ * Also callable standalone for CI/testing: `validateFrameworkConfig(config)`.
80
+ */
81
+ declare function validateFrameworkConfig(cfg: FrameworkConfig): ConfigValidationResult;
82
+ declare function createFrameworkConfig(overrides?: FrameworkConfigOverrides): FrameworkConfig;
83
+ declare function configureFramework(overrides: FrameworkConfigOverrides): void;
84
+ declare function getFrameworkConfig(): FrameworkConfig;
85
+ /**
86
+ * Reset framework config to immutable defaults.
87
+ * Intended for test/integration teardown.
88
+ */
89
+ declare function clearFrameworkConfig(): void;
90
+ /** Max file size for upload — reads from framework config limits.maxFileSize */
91
+ declare function getMaxFileSize(): number;
92
+ /** Debounce delay for autocomplete search — reads from framework config limits.autocompleteDebounceMs */
93
+ declare function getAutocompleteDebounceMs(): number;
94
+ /** Idle timeout for SignalR hub disconnect — reads from framework config limits.signalrIdleTimeoutMs */
95
+ declare function getSignalrIdleTimeoutMs(): number;
96
+ /** SignalR server timeout — reads from framework config limits.signalrServerTimeoutMs */
97
+ declare function getSignalrServerTimeoutMs(): number;
98
+ /** SignalR keep-alive interval — reads from framework config limits.signalrKeepAliveIntervalMs */
99
+ declare function getSignalrKeepAliveIntervalMs(): number;
100
+ /** SignalR reconnect delay sequence — reads from framework config limits.signalrReconnectDelaysMs */
101
+ declare function getSignalrReconnectDelaysMs(): readonly number[];
102
+ /** Idle session timeout — reads from framework config limits.idleSessionTimeoutMs */
103
+ declare function getIdleSessionTimeoutMs(): number;
104
+ /** API request timeout — reads from framework config api.timeoutMs */
105
+ declare function getApiTimeoutMs(): number;
106
+ /** Default list page size — reads from framework config api.defaultPageSize */
107
+ declare function getApiDefaultPageSize(): number;
108
+ declare function getSessionCookieName(prefix: string): string;
109
+ declare function getSecureSessionCookieName(prefix: string): string;
110
+ /**
111
+ * Apply the fallback config to a SpringInstance.
112
+ * Called by SpringProvider after instance creation to pick up early configureFramework() calls.
113
+ */
114
+ declare function applyFallbackConfig(instance: {
115
+ core: {
116
+ config: FrameworkConfig;
117
+ };
118
+ }): void;
119
+
120
+ /**
121
+ * Runtime environment variable keys injected on the client.
122
+ * Values are read from `window.__RUNTIME_ENV__`.
123
+ *
124
+ * Consumers can register additional keys via `registerRuntimeEnvKeys()`.
125
+ * @module runtime-env-config
126
+ */
127
+ /** Default framework runtime env keys. */
128
+ declare const RUNTIME_ENV_KEYS: readonly ["IMAGE_TAG", "ENVIRONMENT", "BUILD_NUM", "BUILD_DATE"];
129
+ type RuntimeEnvKey = (typeof RUNTIME_ENV_KEYS)[number];
130
+ type RuntimeEnv = Partial<Record<string, string>>;
131
+ /**
132
+ * Register additional runtime environment keys.
133
+ * Call this before runtime env payload is consumed by the app bootstrap.
134
+ */
135
+ declare function registerRuntimeEnvKeys(...keys: string[]): void;
136
+ /** Returns all registered runtime env keys (defaults + consumer-registered). */
137
+ declare function getRuntimeEnvKeys(): readonly string[];
138
+ /** Clear consumer-registered runtime env keys. For testing only. */
139
+ declare function clearRuntimeEnvKeys(): void;
140
+ declare global {
141
+ interface Window {
142
+ __RUNTIME_ENV__?: RuntimeEnv;
143
+ }
144
+ }
145
+ declare function getRuntimeVar(key: string): string;
146
+ declare function getImageTag(): string;
147
+
148
+ export { getSignalrReconnectDelaysMs as A, getSignalrServerTimeoutMs as B, type ConfigValidationResult as C, DEFAULT_FRAMEWORK_CONFIG as D, registerRuntimeEnvKeys as E, validateFrameworkConfig as F, RUNTIME_ENV_KEYS as R, configureFramework as a, getCapability as b, configureCapabilities as c, getFrameworkConfig as d, type RuntimeEnv as e, type RuntimeEnvKey as f, getCapabilities as g, hasCapability as h, applyFallbackConfig as i, clearCapabilities as j, clearFrameworkConfig as k, clearRuntimeEnvKeys as l, createFrameworkConfig as m, getApiDefaultPageSize as n, getApiTimeoutMs as o, getAutocompleteDebounceMs as p, getIdleSessionTimeoutMs as q, getImageTag as r, getMaxFileSize as s, getRegisteredCapabilityNames as t, getRuntimeEnvKeys as u, getRuntimeVar as v, getSecureSessionCookieName as w, getSessionCookieName as x, getSignalrIdleTimeoutMs as y, getSignalrKeepAliveIntervalMs as z };
@@ -0,0 +1,9 @@
1
+ type SecurityLogScalar = string | number | boolean | null;
2
+ interface SecurityLogData {
3
+ [key: string]: SecurityLogValue;
4
+ }
5
+ type SecurityLogValue = SecurityLogScalar | SecurityLogData | SecurityLogValue[];
6
+ declare function isSensitiveSecurityKey(key: string): boolean;
7
+ declare function sanitizeSecurityData(data?: SecurityLogData): SecurityLogData | undefined;
8
+
9
+ export { type SecurityLogData as S, type SecurityLogScalar as a, type SecurityLogValue as b, isSensitiveSecurityKey as i, sanitizeSecurityData as s };
@@ -0,0 +1,119 @@
1
+ import { a as FrameworkConfig } from './framework-config-types-DeUbx4bu.js';
2
+ import { M as MiddlewareSlot, b as MiddlewareEntry } from './middleware-types-DVG9C1qJ.js';
3
+ import { V as ValidatorFn } from './form-types-D3MdGpjA.js';
4
+ import { S as SecurityLogData } from './security-sanitize-Bb0PExM6.js';
5
+
6
+ /**
7
+ * SpringInstance — central state container for the SPRING framework.
8
+ * Replaces module-level singletons with a per-instance state tree.
9
+ *
10
+ * IMPORTANT: This file imports only from type-only or non-circular modules.
11
+ * Type imports from config/framework-config-types.ts are safe (pure type file).
12
+ * @module spring-instance
13
+ */
14
+
15
+ /** Content resolution adapter (i18n) */
16
+ interface InstanceContentAdapter {
17
+ getRes(key: string): string;
18
+ }
19
+ /** Notification adapter (toast/snackbar) */
20
+ interface InstanceNotificationAdapter {
21
+ error(msg: string): void;
22
+ success(msg: string): void;
23
+ warning(msg: string): void;
24
+ info(msg: string): void;
25
+ }
26
+ /** Security event types emitted by the framework. Extensible via `string &amp; {}`. */
27
+ type SecurityEventType = "login" | "logout" | "password_change" | "session_timeout" | "rate_limit_exceeded" | "auth_failed" | "session_expired" | "csrf_mismatch" | "token_refresh_failed" | (string & {});
28
+ /** Log adapter (error/warn/security) */
29
+ type BivariantCallback<TArgs extends unknown[], TResult = void> = {
30
+ bivarianceHack: (...args: TArgs) => TResult;
31
+ }["bivarianceHack"];
32
+ interface InstanceLogAdapter {
33
+ onError?: (context: string, error?: unknown) => void;
34
+ onWarn?: (context: string, message?: string) => void;
35
+ onSecurityEvent?: BivariantCallback<[event: SecurityEventType, data?: SecurityLogData]>;
36
+ }
37
+ /**
38
+ * Instance lifecycle state.
39
+ * - `creating`: Instance is being constructed (before init)
40
+ * - `ready`: Instance is fully initialized and operational
41
+ * - `disposing`: Instance is in cleanup process
42
+ * - `disposed`: Instance has been disposed and should not be used
43
+ */
44
+ type InstanceLifecycleState = "creating" | "ready" | "disposing" | "disposed";
45
+ /** Built-in logout reasons. Extensible via `string &amp; {}` to allow custom reasons from plugins. */
46
+ type LogoutReason = "unauthorized" | "proxy_error" | "token_missing" | "manual" | (string & {});
47
+ type LogoutHandler = (reason: LogoutReason) => void | Promise<void>;
48
+ type EventHandler<T = unknown> = (payload: T) => void | Promise<void>;
49
+ interface HandlerEntry<T = unknown> {
50
+ handler: EventHandler<T>;
51
+ once: boolean;
52
+ }
53
+ interface RequestHook {
54
+ (url: string, config: {
55
+ method: string;
56
+ headers: Record<string, string>;
57
+ }): void;
58
+ }
59
+ interface ResponseHook {
60
+ (url: string, status: number, data: unknown): void;
61
+ }
62
+ /**
63
+ * Extensible UI state interface for SpringInstance.
64
+ * Plugins and packages can extend this via declaration merging:
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * declare module "@spring-systems/core/instance" {
69
+ * interface SpringUIState {
70
+ * myPlugin: { count: number };
71
+ * }
72
+ * }
73
+ * ```
74
+ *
75
+ * The base interface includes an index signature to support dynamic
76
+ * registry keys (slots, renderers, etc.) stored with string/symbol keys.
77
+ */
78
+ interface SpringUIState {
79
+ [key: string | symbol]: unknown;
80
+ }
81
+ /** Core framework state — config, adapters, logging, auth, middleware, validation */
82
+ interface SpringCoreState {
83
+ /** Framework configuration — fully typed via FrameworkConfig from framework-config-types.ts */
84
+ config: FrameworkConfig;
85
+ contentAdapter: InstanceContentAdapter;
86
+ notificationAdapter: InstanceNotificationAdapter;
87
+ logAdapter: InstanceLogAdapter;
88
+ logoutHandler: LogoutHandler | null;
89
+ authenticated: boolean;
90
+ logoutInFlight: Promise<void> | null;
91
+ /** Event bus listeners map — keyed by event name. Readonly ref — mutate contents via Map methods, never reassign. */
92
+ readonly eventListeners: Map<string, HandlerEntry[]>;
93
+ readonly middlewareRegistry: Map<MiddlewareSlot, MiddlewareEntry<any>[]>;
94
+ readonly validatorRegistry: Map<string, ValidatorFn>;
95
+ }
96
+ /** API client state — auth tokens, request tracking, hooks */
97
+ interface SpringApiState {
98
+ authToken: string | null;
99
+ /** Readonly ref — mutate contents via Set methods, never reassign. */
100
+ readonly requestControllers: Set<AbortController>;
101
+ requestHook: RequestHook | null;
102
+ responseHook: ResponseHook | null;
103
+ apiPrefix: string;
104
+ sessionExpiredCodes: Set<string> | undefined;
105
+ }
106
+ /** Central framework instance. All mutable state lives here. */
107
+ interface SpringInstance {
108
+ readonly id: string;
109
+ /** Current lifecycle state of this instance */
110
+ readonly state: InstanceLifecycleState;
111
+ readonly core: SpringCoreState;
112
+ readonly api: SpringApiState;
113
+ /** Extensible UI state — plugins extend via declaration merging on SpringUIState */
114
+ readonly ui: SpringUIState;
115
+ /** Dispose this instance — clears all state, aborts requests, runs plugin cleanups */
116
+ dispose(): void;
117
+ }
118
+
119
+ export type { HandlerEntry as H, InstanceContentAdapter as I, LogoutHandler as L, RequestHook as R, SecurityEventType as S, LogoutReason as a, InstanceNotificationAdapter as b, InstanceLogAdapter as c, SpringInstance as d, InstanceLifecycleState as e, ResponseHook as f, SpringApiState as g, SpringCoreState as h, SpringUIState as i };
@@ -0,0 +1,129 @@
1
+ import { F as FrameworkConfigOverrides } from '../framework-config-types-DeUbx4bu.js';
2
+ import { I as InstanceContentAdapter, b as InstanceNotificationAdapter, c as InstanceLogAdapter, d as SpringInstance } from '../spring-instance-EbUh4mQb.js';
3
+ import { SpringPlugin } from '../plugins/index.js';
4
+ import { S as SecurityLogData } from '../security-sanitize-Bb0PExM6.js';
5
+ import '../middleware-types-DVG9C1qJ.js';
6
+ import '../form-types-D3MdGpjA.js';
7
+
8
+ /**
9
+ * Testing utilities for SPRING framework consumers and plugin authors.
10
+ *
11
+ * Provides factory functions to create isolated SpringInstances for tests
12
+ * with pre-configured mocks and adapters.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { createTestInstance, mockAdapters } from "@spring-systems/core/testing";
17
+ *
18
+ * describe("my plugin", () => {
19
+ * let instance: SpringInstance;
20
+ *
21
+ * beforeEach(() => {
22
+ * instance = createTestInstance({
23
+ * config: { app: { name: "Test" } },
24
+ * plugins: [myPlugin],
25
+ * });
26
+ * });
27
+ *
28
+ * afterEach(() => {
29
+ * instance.dispose();
30
+ * });
31
+ *
32
+ * it("registers middleware", () => {
33
+ * expect(getMiddlewareRunner("form:beforeSave")).toBeDefined();
34
+ * });
35
+ * });
36
+ * ```
37
+ *
38
+ * @module test-utils
39
+ */
40
+
41
+ interface CreateTestInstanceOptions {
42
+ /** Framework config overrides for this test instance */
43
+ config?: FrameworkConfigOverrides;
44
+ /** Plugins to apply to the test instance */
45
+ plugins?: SpringPlugin[];
46
+ /** Whether to set this as the global current instance (default: true) */
47
+ setGlobal?: boolean;
48
+ /** Custom content adapter (defaults to identity function) */
49
+ contentAdapter?: InstanceContentAdapter;
50
+ /** Custom notification adapter (defaults to no-op) */
51
+ notificationAdapter?: InstanceNotificationAdapter;
52
+ /** Custom log adapter (defaults to no-op) */
53
+ logAdapter?: InstanceLogAdapter;
54
+ }
55
+ /**
56
+ * Create an isolated SpringInstance for testing.
57
+ * Automatically sets it as the global current instance (unless setGlobal: false).
58
+ * Call `instance.dispose()` in afterEach to clean up.
59
+ */
60
+ declare function createTestInstance(options?: CreateTestInstanceOptions): SpringInstance;
61
+ /** Pre-built mock adapters for testing */
62
+ declare const mockAdapters: {
63
+ /** Content adapter that returns the key itself (identity) */
64
+ readonly content: () => InstanceContentAdapter;
65
+ /** Content adapter that returns a custom mapping */
66
+ readonly contentWithMessages: (messages: Record<string, string>) => InstanceContentAdapter;
67
+ /** Notification adapter that collects all notifications for assertions */
68
+ readonly notificationCollector: () => {
69
+ adapter: {
70
+ error: (msg: string) => number;
71
+ success: (msg: string) => number;
72
+ warning: (msg: string) => number;
73
+ info: (msg: string) => number;
74
+ };
75
+ calls: {
76
+ type: "error" | "success" | "warning" | "info";
77
+ msg: string;
78
+ }[];
79
+ clear: () => void;
80
+ };
81
+ /** Log adapter that collects all log entries for assertions */
82
+ readonly logCollector: () => {
83
+ adapter: {
84
+ onError: (ctx: string, err?: unknown) => number;
85
+ onWarn: (ctx: string, msg?: string) => number;
86
+ onSecurityEvent: (evt: unknown, data?: SecurityLogData) => number;
87
+ };
88
+ errors: {
89
+ context: string;
90
+ error?: unknown;
91
+ }[];
92
+ warnings: {
93
+ context: string;
94
+ message?: string;
95
+ }[];
96
+ securityEvents: {
97
+ event: unknown;
98
+ data?: SecurityLogData;
99
+ }[];
100
+ };
101
+ /** Silent no-op adapters (suppress all output) */
102
+ readonly silent: {
103
+ readonly content: {
104
+ getRes: (key: string) => string;
105
+ };
106
+ readonly notification: {
107
+ error: (_msg: string) => void;
108
+ success: (_msg: string) => void;
109
+ warning: (_msg: string) => void;
110
+ info: (_msg: string) => void;
111
+ };
112
+ readonly log: {};
113
+ };
114
+ };
115
+ /** Boundary cast for tests — replaces the `as unknown as X` pattern in a single place.
116
+ * Usage: `castTo<FormStoreApi>({ getState: vi.fn() })` instead of `{ getState: vi.fn() } as unknown as FormStoreApi`
117
+ */
118
+ declare function castTo<T>(value: unknown): T;
119
+ /** Runtime assertion that a value is defined. Returns the value with a non-nullable type.
120
+ * Usage: `assertDefined(arr[0]).field` instead of `arr[0]!.field`
121
+ */
122
+ declare function assertDefined<T>(val: T | undefined | null, msg?: string): T;
123
+ /**
124
+ * Resets singleton/fallback runtime state across core modules.
125
+ * Useful in integration tests that run multiple app lifecycles in one process.
126
+ */
127
+ declare function resetCoreRuntimeState(): void;
128
+
129
+ export { type CreateTestInstanceOptions, assertDefined, castTo, createTestInstance, mockAdapters, resetCoreRuntimeState };
@@ -0,0 +1,171 @@
1
+ import {
2
+ createSpringInstance
3
+ } from "../chunk-F2SIMWZ5.js";
4
+ import {
5
+ applyPlugins,
6
+ disposePlugins
7
+ } from "../chunk-GXU75LQX.js";
8
+ import {
9
+ eventBus
10
+ } from "../chunk-OSSX443T.js";
11
+ import {
12
+ clearAllMiddleware
13
+ } from "../chunk-MEWPYTWC.js";
14
+ import {
15
+ clearRuntimeEnvKeys
16
+ } from "../chunk-UDT2RPX2.js";
17
+ import {
18
+ clearCapabilities
19
+ } from "../chunk-S7MKRNMI.js";
20
+ import {
21
+ clearFrameworkConfig
22
+ } from "../chunk-F7WUQJH7.js";
23
+ import {
24
+ clearValidators
25
+ } from "../chunk-RRWKDFAB.js";
26
+ import "../chunk-QAVWXARR.js";
27
+ import {
28
+ clearAuthState
29
+ } from "../chunk-HFELOXDQ.js";
30
+ import {
31
+ clearHttpAdapter,
32
+ clearLicenseAdapter,
33
+ clearNotificationAdapter
34
+ } from "../chunk-GON7Q32Q.js";
35
+ import {
36
+ clearTelemetryAdapter
37
+ } from "../chunk-S6RPCN5H.js";
38
+ import {
39
+ clearContentAdapter
40
+ } from "../chunk-NQQIVCLX.js";
41
+ import "../chunk-N2L4TUC4.js";
42
+ import "../chunk-RUCXSQEY.js";
43
+ import {
44
+ clearLogAdapter,
45
+ resetLogLevel
46
+ } from "../chunk-KX32MU3I.js";
47
+ import {
48
+ clearSpringInstance,
49
+ setSpringInstance
50
+ } from "../chunk-EFUBAQCV.js";
51
+ import "../chunk-PT4DIYUK.js";
52
+
53
+ // src/testing/test-utils.ts
54
+ function createTestInstance(options = {}) {
55
+ const instance = createSpringInstance(options.config);
56
+ if (options.contentAdapter) {
57
+ instance.core.contentAdapter = options.contentAdapter;
58
+ }
59
+ if (options.notificationAdapter) {
60
+ instance.core.notificationAdapter = options.notificationAdapter;
61
+ }
62
+ if (options.logAdapter) {
63
+ instance.core.logAdapter = options.logAdapter;
64
+ }
65
+ if (options.setGlobal !== false) {
66
+ setSpringInstance(instance);
67
+ }
68
+ if (options.plugins && options.plugins.length > 0) {
69
+ applyPlugins(instance, options.plugins);
70
+ }
71
+ const originalDispose = instance.dispose.bind(instance);
72
+ instance.dispose = () => {
73
+ disposePlugins(instance);
74
+ originalDispose();
75
+ if (options.setGlobal !== false) {
76
+ clearSpringInstance();
77
+ }
78
+ };
79
+ return instance;
80
+ }
81
+ var mockAdapters = {
82
+ /** Content adapter that returns the key itself (identity) */
83
+ content: () => ({
84
+ getRes: (key) => key
85
+ }),
86
+ /** Content adapter that returns a custom mapping */
87
+ contentWithMessages: (messages) => ({
88
+ getRes: (key) => messages[key] ?? `[${key}]`
89
+ }),
90
+ /** Notification adapter that collects all notifications for assertions */
91
+ notificationCollector: () => {
92
+ const calls = [];
93
+ return {
94
+ adapter: {
95
+ error: (msg) => calls.push({ type: "error", msg }),
96
+ success: (msg) => calls.push({ type: "success", msg }),
97
+ warning: (msg) => calls.push({ type: "warning", msg }),
98
+ info: (msg) => calls.push({ type: "info", msg })
99
+ },
100
+ calls,
101
+ clear: () => {
102
+ calls.length = 0;
103
+ }
104
+ };
105
+ },
106
+ /** Log adapter that collects all log entries for assertions */
107
+ logCollector: () => {
108
+ const errors = [];
109
+ const warnings = [];
110
+ const securityEvents = [];
111
+ return {
112
+ adapter: {
113
+ onError: (ctx, err) => errors.push({ context: ctx, error: err }),
114
+ onWarn: (ctx, msg) => warnings.push({ context: ctx, message: msg }),
115
+ onSecurityEvent: (evt, data) => securityEvents.push({ event: evt, data })
116
+ },
117
+ errors,
118
+ warnings,
119
+ securityEvents
120
+ };
121
+ },
122
+ /** Silent no-op adapters (suppress all output) */
123
+ silent: {
124
+ content: { getRes: (key) => key },
125
+ notification: {
126
+ error: (_msg) => {
127
+ },
128
+ success: (_msg) => {
129
+ },
130
+ warning: (_msg) => {
131
+ },
132
+ info: (_msg) => {
133
+ }
134
+ },
135
+ log: {}
136
+ }
137
+ };
138
+ function castTo(value) {
139
+ return value;
140
+ }
141
+ function assertDefined(val, msg) {
142
+ if (val === void 0 || val === null) {
143
+ throw new Error(msg ?? "Expected value to be defined, but got " + String(val));
144
+ }
145
+ return val;
146
+ }
147
+ function resetCoreRuntimeState() {
148
+ eventBus.clearAll();
149
+ clearAllMiddleware();
150
+ clearValidators();
151
+ clearCapabilities();
152
+ clearFrameworkConfig();
153
+ clearRuntimeEnvKeys();
154
+ clearAuthState();
155
+ clearContentAdapter();
156
+ clearNotificationAdapter();
157
+ clearHttpAdapter();
158
+ clearLicenseAdapter();
159
+ clearTelemetryAdapter();
160
+ clearLogAdapter();
161
+ resetLogLevel();
162
+ clearSpringInstance();
163
+ }
164
+ export {
165
+ assertDefined,
166
+ castTo,
167
+ createTestInstance,
168
+ mockAdapters,
169
+ resetCoreRuntimeState
170
+ };
171
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/testing/test-utils.ts"],"sourcesContent":["/**\n * Testing utilities for SPRING framework consumers and plugin authors.\n *\n * Provides factory functions to create isolated SpringInstances for tests\n * with pre-configured mocks and adapters.\n *\n * @example\n * ```ts\n * import { createTestInstance, mockAdapters } from \"@spring-systems/core/testing\";\n *\n * describe(\"my plugin\", () => {\n * let instance: SpringInstance;\n *\n * beforeEach(() => {\n * instance = createTestInstance({\n * config: { app: { name: \"Test\" } },\n * plugins: [myPlugin],\n * });\n * });\n *\n * afterEach(() => {\n * instance.dispose();\n * });\n *\n * it(\"registers middleware\", () => {\n * expect(getMiddlewareRunner(\"form:beforeSave\")).toBeDefined();\n * });\n * });\n * ```\n *\n * @module test-utils\n */\n\nimport {\n clearContentAdapter,\n clearHttpAdapter,\n clearLicenseAdapter,\n clearNotificationAdapter,\n clearTelemetryAdapter,\n} from \"../adapters\";\nimport { clearAuthState } from \"../auth/auth-handler\";\nimport { clearCapabilities } from \"../config/capabilities\";\nimport { clearFrameworkConfig } from \"../config/framework-config\";\nimport type { FrameworkConfigOverrides } from \"../config/framework-config-types\";\nimport { clearRuntimeEnvKeys } from \"../config/runtime-env-config\";\nimport { eventBus } from \"../events/event-bus\";\nimport { createSpringInstance } from \"../instance/create-instance\";\nimport { clearSpringInstance,setSpringInstance } from \"../instance/current-instance\";\nimport type { InstanceContentAdapter, InstanceLogAdapter,InstanceNotificationAdapter,SpringInstance } from \"../instance/spring-instance\";\nimport { clearLogAdapter, resetLogLevel } from \"../logger/logger\";\nimport { clearAllMiddleware } from \"../middleware/middleware-registry\";\nimport type { SpringPlugin } from \"../plugins/plugin\";\nimport { applyPlugins, disposePlugins } from \"../plugins/plugin\";\nimport type { SecurityLogData } from \"../utils/security-sanitize\";\nimport { clearValidators } from \"../validation/validations\";\n\nexport interface CreateTestInstanceOptions {\n /** Framework config overrides for this test instance */\n config?: FrameworkConfigOverrides;\n /** Plugins to apply to the test instance */\n plugins?: SpringPlugin[];\n /** Whether to set this as the global current instance (default: true) */\n setGlobal?: boolean;\n /** Custom content adapter (defaults to identity function) */\n contentAdapter?: InstanceContentAdapter;\n /** Custom notification adapter (defaults to no-op) */\n notificationAdapter?: InstanceNotificationAdapter;\n /** Custom log adapter (defaults to no-op) */\n logAdapter?: InstanceLogAdapter;\n}\n\n/**\n * Create an isolated SpringInstance for testing.\n * Automatically sets it as the global current instance (unless setGlobal: false).\n * Call `instance.dispose()` in afterEach to clean up.\n */\nexport function createTestInstance(options: CreateTestInstanceOptions = {}): SpringInstance {\n const instance = createSpringInstance(options.config);\n\n // Apply mock adapters\n if (options.contentAdapter) {\n instance.core.contentAdapter = options.contentAdapter;\n }\n if (options.notificationAdapter) {\n instance.core.notificationAdapter = options.notificationAdapter;\n }\n if (options.logAdapter) {\n instance.core.logAdapter = options.logAdapter;\n }\n\n // Set as global if requested\n if (options.setGlobal !== false) {\n setSpringInstance(instance);\n }\n\n // Apply plugins\n if (options.plugins && options.plugins.length > 0) {\n applyPlugins(instance, options.plugins);\n }\n\n // Augment dispose to also clear global and dispose plugins\n const originalDispose = instance.dispose.bind(instance);\n instance.dispose = () => {\n disposePlugins(instance);\n originalDispose();\n if (options.setGlobal !== false) {\n clearSpringInstance();\n }\n };\n\n return instance;\n}\n\n/** Pre-built mock adapters for testing */\nexport const mockAdapters = {\n /** Content adapter that returns the key itself (identity) */\n content: (): InstanceContentAdapter => ({\n getRes: (key: string) => key,\n }),\n\n /** Content adapter that returns a custom mapping */\n contentWithMessages: (messages: Record<string, string>): InstanceContentAdapter => ({\n getRes: (key: string) => messages[key] ?? `[${key}]`,\n }),\n\n /** Notification adapter that collects all notifications for assertions */\n notificationCollector: () => {\n const calls: Array<{ type: \"error\" | \"success\" | \"warning\" | \"info\"; msg: string }> = [];\n return {\n adapter: {\n error: (msg: string) => calls.push({ type: \"error\", msg }),\n success: (msg: string) => calls.push({ type: \"success\", msg }),\n warning: (msg: string) => calls.push({ type: \"warning\", msg }),\n info: (msg: string) => calls.push({ type: \"info\", msg }),\n } satisfies InstanceNotificationAdapter,\n calls,\n clear: () => { calls.length = 0; },\n };\n },\n\n /** Log adapter that collects all log entries for assertions */\n logCollector: () => {\n const errors: Array<{ context: string; error?: unknown }> = [];\n const warnings: Array<{ context: string; message?: string }> = [];\n const securityEvents: Array<{ event: unknown; data?: SecurityLogData }> = [];\n return {\n adapter: {\n onError: (ctx: string, err?: unknown) => errors.push({ context: ctx, error: err }),\n onWarn: (ctx: string, msg?: string) => warnings.push({ context: ctx, message: msg }),\n onSecurityEvent: (evt: unknown, data?: SecurityLogData) => securityEvents.push({ event: evt, data }),\n } satisfies InstanceLogAdapter,\n errors,\n warnings,\n securityEvents,\n };\n },\n\n /** Silent no-op adapters (suppress all output) */\n silent: {\n content: { getRes: (key: string) => key } satisfies InstanceContentAdapter,\n notification: {\n error: (_msg: string) => {},\n success: (_msg: string) => {},\n warning: (_msg: string) => {},\n info: (_msg: string) => {},\n } satisfies InstanceNotificationAdapter,\n log: {} satisfies InstanceLogAdapter,\n },\n} as const;\n\n/** Boundary cast for tests — replaces the `as unknown as X` pattern in a single place.\n * Usage: `castTo<FormStoreApi>({ getState: vi.fn() })` instead of `{ getState: vi.fn() } as unknown as FormStoreApi`\n */\nexport function castTo<T>(value: unknown): T {\n return value as T;\n}\n\n/** Runtime assertion that a value is defined. Returns the value with a non-nullable type.\n * Usage: `assertDefined(arr[0]).field` instead of `arr[0]!.field`\n */\nexport function assertDefined<T>(val: T | undefined | null, msg?: string): T {\n if (val === undefined || val === null) {\n throw new Error(msg ?? \"Expected value to be defined, but got \" + String(val));\n }\n return val;\n}\n\n/**\n * Resets singleton/fallback runtime state across core modules.\n * Useful in integration tests that run multiple app lifecycles in one process.\n */\nexport function resetCoreRuntimeState(): void {\n eventBus.clearAll();\n clearAllMiddleware();\n clearValidators();\n clearCapabilities();\n clearFrameworkConfig();\n clearRuntimeEnvKeys();\n\n clearAuthState();\n clearContentAdapter();\n clearNotificationAdapter();\n clearHttpAdapter();\n clearLicenseAdapter();\n clearTelemetryAdapter();\n clearLogAdapter();\n resetLogLevel();\n\n clearSpringInstance();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EO,SAAS,mBAAmB,UAAqC,CAAC,GAAmB;AACxF,QAAM,WAAW,qBAAqB,QAAQ,MAAM;AAGpD,MAAI,QAAQ,gBAAgB;AACxB,aAAS,KAAK,iBAAiB,QAAQ;AAAA,EAC3C;AACA,MAAI,QAAQ,qBAAqB;AAC7B,aAAS,KAAK,sBAAsB,QAAQ;AAAA,EAChD;AACA,MAAI,QAAQ,YAAY;AACpB,aAAS,KAAK,aAAa,QAAQ;AAAA,EACvC;AAGA,MAAI,QAAQ,cAAc,OAAO;AAC7B,sBAAkB,QAAQ;AAAA,EAC9B;AAGA,MAAI,QAAQ,WAAW,QAAQ,QAAQ,SAAS,GAAG;AAC/C,iBAAa,UAAU,QAAQ,OAAO;AAAA,EAC1C;AAGA,QAAM,kBAAkB,SAAS,QAAQ,KAAK,QAAQ;AACtD,WAAS,UAAU,MAAM;AACrB,mBAAe,QAAQ;AACvB,oBAAgB;AAChB,QAAI,QAAQ,cAAc,OAAO;AAC7B,0BAAoB;AAAA,IACxB;AAAA,EACJ;AAEA,SAAO;AACX;AAGO,IAAM,eAAe;AAAA;AAAA,EAExB,SAAS,OAA+B;AAAA,IACpC,QAAQ,CAAC,QAAgB;AAAA,EAC7B;AAAA;AAAA,EAGA,qBAAqB,CAAC,cAA8D;AAAA,IAChF,QAAQ,CAAC,QAAgB,SAAS,GAAG,KAAK,IAAI,GAAG;AAAA,EACrD;AAAA;AAAA,EAGA,uBAAuB,MAAM;AACzB,UAAM,QAAgF,CAAC;AACvF,WAAO;AAAA,MACH,SAAS;AAAA,QACL,OAAO,CAAC,QAAgB,MAAM,KAAK,EAAE,MAAM,SAAS,IAAI,CAAC;AAAA,QACzD,SAAS,CAAC,QAAgB,MAAM,KAAK,EAAE,MAAM,WAAW,IAAI,CAAC;AAAA,QAC7D,SAAS,CAAC,QAAgB,MAAM,KAAK,EAAE,MAAM,WAAW,IAAI,CAAC;AAAA,QAC7D,MAAM,CAAC,QAAgB,MAAM,KAAK,EAAE,MAAM,QAAQ,IAAI,CAAC;AAAA,MAC3D;AAAA,MACA;AAAA,MACA,OAAO,MAAM;AAAE,cAAM,SAAS;AAAA,MAAG;AAAA,IACrC;AAAA,EACJ;AAAA;AAAA,EAGA,cAAc,MAAM;AAChB,UAAM,SAAsD,CAAC;AAC7D,UAAM,WAAyD,CAAC;AAChE,UAAM,iBAAoE,CAAC;AAC3E,WAAO;AAAA,MACH,SAAS;AAAA,QACL,SAAS,CAAC,KAAa,QAAkB,OAAO,KAAK,EAAE,SAAS,KAAK,OAAO,IAAI,CAAC;AAAA,QACjF,QAAQ,CAAC,KAAa,QAAiB,SAAS,KAAK,EAAE,SAAS,KAAK,SAAS,IAAI,CAAC;AAAA,QACnF,iBAAiB,CAAC,KAAc,SAA2B,eAAe,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC;AAAA,MACvG;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA,EAGA,QAAQ;AAAA,IACJ,SAAS,EAAE,QAAQ,CAAC,QAAgB,IAAI;AAAA,IACxC,cAAc;AAAA,MACV,OAAO,CAAC,SAAiB;AAAA,MAAC;AAAA,MAC1B,SAAS,CAAC,SAAiB;AAAA,MAAC;AAAA,MAC5B,SAAS,CAAC,SAAiB;AAAA,MAAC;AAAA,MAC5B,MAAM,CAAC,SAAiB;AAAA,MAAC;AAAA,IAC7B;AAAA,IACA,KAAK,CAAC;AAAA,EACV;AACJ;AAKO,SAAS,OAAU,OAAmB;AACzC,SAAO;AACX;AAKO,SAAS,cAAiB,KAA2B,KAAiB;AACzE,MAAI,QAAQ,UAAa,QAAQ,MAAM;AACnC,UAAM,IAAI,MAAM,OAAO,2CAA2C,OAAO,GAAG,CAAC;AAAA,EACjF;AACA,SAAO;AACX;AAMO,SAAS,wBAA8B;AAC1C,WAAS,SAAS;AAClB,qBAAmB;AACnB,kBAAgB;AAChB,oBAAkB;AAClB,uBAAqB;AACrB,sBAAoB;AAEpB,iBAAe;AACf,sBAAoB;AACpB,2BAAyB;AACzB,mBAAiB;AACjB,sBAAoB;AACpB,wBAAsB;AACtB,kBAAgB;AAChB,gBAAc;AAEd,sBAAoB;AACxB;","names":[]}