@raubjo/architect-core 0.1.0 → 0.1.2

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 (67) hide show
  1. package/README.md +216 -0
  2. package/bun.lock +82 -1
  3. package/package.json +55 -6
  4. package/src/cache/cache.ts +2 -2
  5. package/src/cache/manager.ts +93 -83
  6. package/src/config/adapters/esm.ts +26 -0
  7. package/src/config/clone.ts +5 -5
  8. package/src/config/env.global.d.ts +2 -1
  9. package/src/config/env.ts +53 -55
  10. package/src/config/env_test.helpers.ts +58 -0
  11. package/src/config/repository.ts +180 -142
  12. package/src/container/adapters/builtin.ts +347 -0
  13. package/src/container/adapters/inversify.ts +123 -0
  14. package/src/container/contract.ts +58 -0
  15. package/src/container/runtime.ts +149 -0
  16. package/src/filesystem/adapters/local.ts +92 -83
  17. package/src/filesystem/adapters/local_test.helpers.ts +50 -0
  18. package/src/filesystem/filesystem.ts +11 -11
  19. package/src/foundation/application.ts +205 -175
  20. package/src/foundation/application_test.helpers.ts +31 -0
  21. package/src/index.ts +15 -6
  22. package/src/react.ts +2 -0
  23. package/src/renderers/adapters/react.tsx +35 -0
  24. package/src/renderers/adapters/solid.tsx +32 -0
  25. package/src/renderers/adapters/svelte.ts +70 -0
  26. package/src/renderers/adapters/vue.ts +28 -0
  27. package/src/renderers/contract.ts +15 -0
  28. package/src/runtimes/react.tsx +24 -12
  29. package/src/runtimes/solid.tsx +30 -0
  30. package/src/runtimes/svelte.ts +23 -0
  31. package/src/runtimes/vue.ts +20 -0
  32. package/src/solid.ts +2 -0
  33. package/src/storage/adapters/contract.ts +10 -0
  34. package/src/storage/adapters/indexed-db.ts +170 -156
  35. package/src/storage/adapters/local-storage.ts +34 -34
  36. package/src/storage/adapters/memory.ts +25 -25
  37. package/src/storage/manager.ts +65 -61
  38. package/src/storage/storage.ts +1 -8
  39. package/src/support/facades/cache.ts +40 -40
  40. package/src/support/facades/config.ts +78 -48
  41. package/src/support/facades/facade.ts +43 -28
  42. package/src/support/facades/storage.ts +41 -41
  43. package/src/support/service-provider.ts +11 -11
  44. package/src/support/str.ts +94 -90
  45. package/src/support/str_test.helpers.ts +26 -0
  46. package/src/svelte.ts +2 -0
  47. package/src/vue.ts +2 -0
  48. package/tsconfig.json +16 -0
  49. package/coverage/lcov.info +0 -1078
  50. package/src/config/app.ts +0 -5
  51. package/src/rendering/adapters/react.tsx +0 -27
  52. package/src/rendering/renderer.ts +0 -13
  53. package/src/support/providers/config-service-provider.ts +0 -19
  54. package/tests/application.test.ts +0 -236
  55. package/tests/cache-facade.test.ts +0 -45
  56. package/tests/cache.test.ts +0 -68
  57. package/tests/config-clone.test.ts +0 -31
  58. package/tests/config-env.test.ts +0 -88
  59. package/tests/config-facade.test.ts +0 -96
  60. package/tests/config-repository.test.ts +0 -124
  61. package/tests/facade-base.test.ts +0 -80
  62. package/tests/filesystem.test.ts +0 -81
  63. package/tests/runtime-react.test.tsx +0 -37
  64. package/tests/service-provider.test.ts +0 -23
  65. package/tests/storage-facade.test.ts +0 -46
  66. package/tests/storage.test.ts +0 -264
  67. package/tests/str.test.ts +0 -73
@@ -1,9 +1,9 @@
1
- import type { ConfigItems } from "./repository";
1
+ import type { ConfigItems } from "@/config/repository";
2
2
 
3
3
  export function cloneConfigItems(items: ConfigItems): ConfigItems {
4
- if (typeof structuredClone === "function") {
5
- return structuredClone(items) as ConfigItems;
6
- }
4
+ if (typeof structuredClone === "function") {
5
+ return structuredClone(items) as ConfigItems;
6
+ }
7
7
 
8
- return JSON.parse(JSON.stringify(items)) as ConfigItems;
8
+ return JSON.parse(JSON.stringify(items)) as ConfigItems;
9
9
  }
@@ -1,5 +1,6 @@
1
1
  declare global {
2
- function env<T = unknown>(key: string, defaultValue?: T | null): T | unknown | null;
2
+ function env(key: string): import("./env").EnvValue | undefined;
3
+ function env<T>(key: string, defaultValue: T): import("./env").EnvValue | T;
3
4
  }
4
5
 
5
6
  export {};
package/src/config/env.ts CHANGED
@@ -1,79 +1,77 @@
1
- type EnvMap = Record<string, unknown>;
1
+ export type EnvValue = string | number | boolean | null;
2
+ type EnvMap = Record<string, EnvValue | undefined>;
2
3
 
3
- function normalizeEnvValue(value: unknown): unknown {
4
- if (typeof value !== "string") {
5
- return value;
6
- }
4
+ function normalizeEnvValue(value: EnvValue | undefined): EnvValue | undefined {
5
+ if (typeof value !== "string") {
6
+ return value;
7
+ }
7
8
 
8
- const normalized = value.trim().toLowerCase();
9
- if (normalized === "true" || normalized === "(true)") {
10
- return true;
11
- }
9
+ const normalized = value.trim().toLowerCase();
10
+ if (normalized === "true" || normalized === "(true)") {
11
+ return true;
12
+ }
12
13
 
13
- if (normalized === "false" || normalized === "(false)") {
14
- return false;
15
- }
14
+ if (normalized === "false" || normalized === "(false)") {
15
+ return false;
16
+ }
16
17
 
17
- if (normalized === "null" || normalized === "(null)") {
18
- return null;
19
- }
18
+ if (normalized === "null" || normalized === "(null)") {
19
+ return null;
20
+ }
20
21
 
21
- if (normalized === "empty" || normalized === "(empty)") {
22
- return "";
23
- }
22
+ if (normalized === "empty" || normalized === "(empty)") {
23
+ return "";
24
+ }
24
25
 
25
- return value;
26
+ return value;
26
27
  }
27
28
 
28
29
  function resolveProcessEnv(): EnvMap {
29
- const processValue = (
30
- globalThis as { process?: { env?: EnvMap } }
31
- ).process;
30
+ const processValue = (globalThis as { process?: { env?: EnvMap } }).process;
32
31
 
33
- if (!processValue?.env) {
34
- return {};
35
- }
32
+ if (!processValue?.env) {
33
+ return {};
34
+ }
36
35
 
37
- return processValue.env;
36
+ return processValue.env;
38
37
  }
39
38
 
40
39
  function resolveImportMetaEnv(): EnvMap {
41
- const testEnv = (
42
- globalThis as {
43
- __iocImportMetaEnvForTests?: EnvMap;
40
+ const testEnv = (
41
+ globalThis as {
42
+ __iocImportMetaEnvForTests?: EnvMap;
43
+ }
44
+ ).__iocImportMetaEnvForTests;
45
+ if (testEnv) {
46
+ return testEnv;
44
47
  }
45
- ).__iocImportMetaEnvForTests;
46
- if (testEnv) {
47
- return testEnv;
48
- }
49
48
 
50
- const meta = import.meta as ImportMeta & { env?: EnvMap };
51
- return meta.env ?? {};
49
+ const meta = import.meta as ImportMeta & { env?: EnvMap };
50
+ return meta.env ?? {};
52
51
  }
53
52
 
54
- export function env<T = unknown>(key: string, defaultValue: T | null = null): T | unknown | null {
55
- const importMetaEnv = resolveImportMetaEnv();
56
- if (key in importMetaEnv) {
57
- return normalizeEnvValue(importMetaEnv[key]);
58
- }
53
+ export function env(key: string): EnvValue | undefined;
54
+ export function env<T>(key: string, defaultValue: T): EnvValue | T;
55
+ export function env<T>(
56
+ key: string,
57
+ defaultValue?: T,
58
+ ): EnvValue | T | undefined {
59
+ const importMetaEnv = resolveImportMetaEnv();
60
+ if (key in importMetaEnv) {
61
+ return normalizeEnvValue(importMetaEnv[key]);
62
+ }
59
63
 
60
- const processEnv = resolveProcessEnv();
61
- if (key in processEnv) {
62
- return normalizeEnvValue(processEnv[key]);
63
- }
64
+ const processEnv = resolveProcessEnv();
65
+ if (key in processEnv) {
66
+ return normalizeEnvValue(processEnv[key]);
67
+ }
64
68
 
65
- return defaultValue;
69
+ return defaultValue;
66
70
  }
67
71
 
68
72
  export function registerGlobalEnv(): void {
69
- const globalScope = globalThis as { env?: typeof env };
70
- if (typeof globalScope.env !== "function") {
71
- globalScope.env = env;
72
- }
73
+ const globalScope = globalThis as { env?: typeof env };
74
+ if (typeof globalScope.env !== "function") {
75
+ globalScope.env = env;
76
+ }
73
77
  }
74
-
75
- export const __envTesting = {
76
- normalizeEnvValue,
77
- resolveImportMetaEnv,
78
- resolveProcessEnv,
79
- };
@@ -0,0 +1,58 @@
1
+ import type { EnvValue } from "./env";
2
+
3
+ type EnvMap = Record<string, EnvValue | undefined>;
4
+
5
+ function normalizeEnvValue(value: EnvValue | undefined): EnvValue | undefined {
6
+ if (typeof value !== "string") {
7
+ return value;
8
+ }
9
+
10
+ const normalized = value.trim().toLowerCase();
11
+ if (normalized === "true" || normalized === "(true)") {
12
+ return true;
13
+ }
14
+
15
+ if (normalized === "false" || normalized === "(false)") {
16
+ return false;
17
+ }
18
+
19
+ if (normalized === "null" || normalized === "(null)") {
20
+ return null;
21
+ }
22
+
23
+ if (normalized === "empty" || normalized === "(empty)") {
24
+ return "";
25
+ }
26
+
27
+ return value;
28
+ }
29
+
30
+ function resolveProcessEnv(): EnvMap {
31
+ const processValue = (globalThis as { process?: { env?: EnvMap } }).process;
32
+
33
+ if (!processValue?.env) {
34
+ return {};
35
+ }
36
+
37
+ return processValue.env;
38
+ }
39
+
40
+ function resolveImportMetaEnv(): EnvMap {
41
+ const testEnv = (
42
+ globalThis as {
43
+ __iocImportMetaEnvForTests?: EnvMap;
44
+ }
45
+ ).__iocImportMetaEnvForTests;
46
+ if (testEnv) {
47
+ return testEnv;
48
+ }
49
+
50
+ const meta = import.meta as ImportMeta & { env?: EnvMap };
51
+ return meta.env ?? {};
52
+ }
53
+
54
+ export const envTestingHelpers = {
55
+ normalizeEnvValue,
56
+ resolveImportMetaEnv,
57
+ resolveProcessEnv,
58
+ };
@@ -2,203 +2,241 @@ export type ConfigItems = Record<string, unknown>;
2
2
  export type ConfigDefaults = Record<string | number, unknown>;
3
3
 
4
4
  function isPlainObject(value: unknown): value is Record<string, unknown> {
5
- return typeof value === "object" && value !== null && !Array.isArray(value);
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
6
  }
7
7
 
8
8
  function resolveDefault<T>(defaultValue: T | (() => T)): T {
9
- return typeof defaultValue === "function" ? (defaultValue as () => T)() : defaultValue;
9
+ return typeof defaultValue === "function" ?
10
+ (defaultValue as () => T)()
11
+ : defaultValue;
10
12
  }
11
13
 
12
- function dataGet(source: unknown, path: string, defaultValue: unknown = null): unknown {
13
- if (!path) {
14
- return source;
15
- }
14
+ function dataGet(
15
+ source: unknown,
16
+ path: string,
17
+ defaultValue: unknown = null,
18
+ ): unknown {
19
+ if (!path) {
20
+ return source;
21
+ }
16
22
 
17
- const segments = path.split(".");
18
- let cursor: unknown = source;
23
+ const segments = path.split(".");
24
+ let cursor: unknown = source;
19
25
 
20
- for (const segment of segments) {
21
- if (isPlainObject(cursor) && segment in cursor) {
22
- cursor = cursor[segment];
23
- continue;
24
- }
26
+ for (const segment of segments) {
27
+ if (isPlainObject(cursor) && segment in cursor) {
28
+ cursor = cursor[segment];
29
+ continue;
30
+ }
25
31
 
26
- return resolveDefault(defaultValue);
27
- }
32
+ return resolveDefault(defaultValue);
33
+ }
28
34
 
29
- return cursor;
35
+ return cursor;
30
36
  }
31
37
 
32
- function dataSet(target: Record<string, unknown>, path: string, value: unknown): void {
33
- const segments = path.split(".");
34
- let cursor: Record<string, unknown> = target;
38
+ function dataSet(
39
+ target: Record<string, unknown>,
40
+ path: string,
41
+ value: unknown,
42
+ ): void {
43
+ const segments = path.split(".");
44
+ let cursor: Record<string, unknown> = target;
35
45
 
36
- for (let i = 0; i < segments.length; i += 1) {
37
- const segment = segments[i];
38
- const isLast = i === segments.length - 1;
46
+ for (let i = 0; i < segments.length; i += 1) {
47
+ const segment = segments[i];
48
+ const isLast = i === segments.length - 1;
39
49
 
40
- if (isLast) {
41
- cursor[segment] = value;
42
- return;
43
- }
50
+ if (isLast) {
51
+ cursor[segment] = value;
52
+ return;
53
+ }
44
54
 
45
- if (!isPlainObject(cursor[segment])) {
46
- cursor[segment] = {};
47
- }
55
+ if (!isPlainObject(cursor[segment])) {
56
+ cursor[segment] = {};
57
+ }
48
58
 
49
- cursor = cursor[segment] as Record<string, unknown>;
50
- }
59
+ cursor = cursor[segment] as Record<string, unknown>;
60
+ }
51
61
  }
52
62
 
53
63
  function dataForget(target: Record<string, unknown>, path: string): void {
54
- const segments = path.split(".");
55
- let cursor: Record<string, unknown> = target;
64
+ const segments = path.split(".");
65
+ let cursor: Record<string, unknown> = target;
56
66
 
57
- for (let i = 0; i < segments.length; i += 1) {
58
- const segment = segments[i];
59
- const isLast = i === segments.length - 1;
67
+ for (let i = 0; i < segments.length; i += 1) {
68
+ const segment = segments[i];
69
+ const isLast = i === segments.length - 1;
60
70
 
61
- if (isLast) {
62
- delete cursor[segment];
63
- return;
64
- }
71
+ if (isLast) {
72
+ delete cursor[segment];
73
+ return;
74
+ }
65
75
 
66
- if (!isPlainObject(cursor[segment])) {
67
- return;
68
- }
76
+ if (!isPlainObject(cursor[segment])) {
77
+ return;
78
+ }
69
79
 
70
- cursor = cursor[segment] as Record<string, unknown>;
71
- }
80
+ cursor = cursor[segment] as Record<string, unknown>;
81
+ }
72
82
  }
73
83
 
74
84
  export default class ConfigRepository {
75
- protected items: ConfigItems;
76
-
77
- constructor(items: ConfigItems = {}) {
78
- this.items = items;
79
- }
85
+ protected items: ConfigItems;
80
86
 
81
- has(key: string | string[]): boolean {
82
- const keys = Array.isArray(key) ? key : [key];
83
- for (const configKey of keys) {
84
- if (this.get(configKey) == null) {
85
- return false;
86
- }
87
+ constructor(items: ConfigItems = {}) {
88
+ this.items = items;
87
89
  }
88
90
 
89
- return true;
90
- }
91
+ has(key: string | string[]): boolean {
92
+ const keys = Array.isArray(key) ? key : [key];
93
+ for (const configKey of keys) {
94
+ if (this.get(configKey) == null) {
95
+ return false;
96
+ }
97
+ }
91
98
 
92
- get<T = unknown>(
93
- key: string | string[],
94
- defaultValue: T | (() => T) | null = null,
95
- ): T | Record<string, unknown> | null {
96
- if (Array.isArray(key)) {
97
- return this.getMany(key);
99
+ return true;
98
100
  }
99
101
 
100
- return dataGet(this.items, key, defaultValue) as T | null;
101
- }
102
-
103
- getMany(keys: string[] | ConfigDefaults): Record<string, unknown> {
104
- const results: Record<string, unknown> = {};
105
-
106
- if (Array.isArray(keys)) {
107
- for (const key of keys) {
108
- results[key] = this.get(key);
109
- }
110
-
111
- return results;
102
+ get<T = unknown>(
103
+ key: string,
104
+ defaultValue?: T | (() => T) | null,
105
+ ): T | null;
106
+ get(key: string[]): Record<string, unknown>;
107
+ get<T = unknown>(
108
+ key: string | string[],
109
+ defaultValue: T | (() => T) | null = null,
110
+ ): T | Record<string, unknown> | null {
111
+ if (Array.isArray(key)) {
112
+ return this.getMany(key);
113
+ }
114
+
115
+ return dataGet(this.items, key, defaultValue) as T | null;
112
116
  }
113
117
 
114
- for (const [key, defaultValue] of Object.entries(keys)) {
115
- results[key] = this.get(key, defaultValue);
116
- }
118
+ getMany(keys: string[] | ConfigDefaults): Record<string, unknown> {
119
+ const results: Record<string, unknown> = {};
117
120
 
118
- return results;
119
- }
121
+ if (Array.isArray(keys)) {
122
+ for (const key of keys) {
123
+ results[key] = this.get(key);
124
+ }
120
125
 
121
- string(key: string, defaultValue: string | (() => string) | null = null): string {
122
- const value = this.get<string>(key, defaultValue);
123
- if (typeof value !== "string") {
124
- throw new TypeError(`Configuration value [${key}] is not a string.`);
125
- }
126
+ return results;
127
+ }
126
128
 
127
- return value;
128
- }
129
+ for (const [key, defaultValue] of Object.entries(keys)) {
130
+ results[key] = this.get(key, defaultValue);
131
+ }
129
132
 
130
- integer(key: string, defaultValue: number | (() => number) | null = null): number {
131
- const value = this.get<number>(key, defaultValue);
132
- if (!Number.isInteger(value)) {
133
- throw new TypeError(`Configuration value [${key}] is not an integer.`);
133
+ return results;
134
134
  }
135
135
 
136
- return value;
137
- }
136
+ string(
137
+ key: string,
138
+ defaultValue: string | (() => string) | null = null,
139
+ ): string {
140
+ const value = this.get<string>(key, defaultValue);
141
+ if (typeof value !== "string") {
142
+ throw new TypeError(
143
+ `Configuration value [${key}] is not a string.`,
144
+ );
145
+ }
146
+
147
+ return value;
148
+ }
138
149
 
139
- float(key: string, defaultValue: number | (() => number) | null = null): number {
140
- const value = this.get<number>(key, defaultValue);
141
- if (typeof value !== "number" || Number.isNaN(value)) {
142
- throw new TypeError(`Configuration value [${key}] is not a float.`);
150
+ integer(
151
+ key: string,
152
+ defaultValue: number | (() => number) | null = null,
153
+ ): number {
154
+ const value = this.get<number>(key, defaultValue);
155
+ if (typeof value !== "number" || !Number.isInteger(value)) {
156
+ throw new TypeError(
157
+ `Configuration value [${key}] is not an integer.`,
158
+ );
159
+ }
160
+
161
+ return value;
143
162
  }
144
163
 
145
- return value;
146
- }
164
+ float(
165
+ key: string,
166
+ defaultValue: number | (() => number) | null = null,
167
+ ): number {
168
+ const value = this.get<number>(key, defaultValue);
169
+ if (typeof value !== "number" || Number.isNaN(value)) {
170
+ throw new TypeError(`Configuration value [${key}] is not a float.`);
171
+ }
147
172
 
148
- boolean(key: string, defaultValue: boolean | (() => boolean) | null = null): boolean {
149
- const value = this.get<boolean>(key, defaultValue);
150
- if (typeof value !== "boolean") {
151
- throw new TypeError(`Configuration value [${key}] is not a boolean.`);
173
+ return value;
152
174
  }
153
175
 
154
- return value;
155
- }
156
-
157
- array<T = unknown>(key: string, defaultValue: T[] | (() => T[]) | null = null): T[] {
158
- const value = this.get<T[]>(key, defaultValue);
159
- if (!Array.isArray(value)) {
160
- throw new TypeError(`Configuration value [${key}] is not an array.`);
176
+ boolean(
177
+ key: string,
178
+ defaultValue: boolean | (() => boolean) | null = null,
179
+ ): boolean {
180
+ const value = this.get<boolean>(key, defaultValue);
181
+ if (typeof value !== "boolean") {
182
+ throw new TypeError(
183
+ `Configuration value [${key}] is not a boolean.`,
184
+ );
185
+ }
186
+
187
+ return value;
161
188
  }
162
189
 
163
- return value;
164
- }
190
+ array<T = unknown>(
191
+ key: string,
192
+ defaultValue: T[] | (() => T[]) | null = null,
193
+ ): T[] {
194
+ const value = this.get<T[]>(key, defaultValue);
195
+ if (!Array.isArray(value)) {
196
+ throw new TypeError(
197
+ `Configuration value [${key}] is not an array.`,
198
+ );
199
+ }
200
+
201
+ return value;
202
+ }
165
203
 
166
- set(key: string | ConfigItems, value: unknown = null): void {
167
- const payload = isPlainObject(key) ? key : { [key]: value };
204
+ set(key: string | ConfigItems, value: unknown = null): void {
205
+ const payload = isPlainObject(key) ? key : { [key]: value };
168
206
 
169
- for (const [configKey, configValue] of Object.entries(payload)) {
170
- dataSet(this.items, configKey, configValue);
207
+ for (const [configKey, configValue] of Object.entries(payload)) {
208
+ dataSet(this.items, configKey, configValue);
209
+ }
171
210
  }
172
- }
173
211
 
174
- prepend(key: string, value: unknown): void {
175
- const values = this.array<unknown>(key, []);
176
- this.set(key, [value, ...values]);
177
- }
212
+ prepend(key: string, value: unknown): void {
213
+ const values = this.array<unknown>(key, []);
214
+ this.set(key, [value, ...values]);
215
+ }
178
216
 
179
- push(key: string, value: unknown): void {
180
- const values = this.array<unknown>(key, []);
181
- values.push(value);
182
- this.set(key, values);
183
- }
217
+ push(key: string, value: unknown): void {
218
+ const values = this.array<unknown>(key, []);
219
+ values.push(value);
220
+ this.set(key, values);
221
+ }
184
222
 
185
- all(): ConfigItems {
186
- return this.items;
187
- }
223
+ all(): ConfigItems {
224
+ return this.items;
225
+ }
188
226
 
189
- offsetExists(key: string): boolean {
190
- return this.has(key);
191
- }
227
+ offsetExists(key: string): boolean {
228
+ return this.has(key);
229
+ }
192
230
 
193
- offsetGet<T = unknown>(key: string): T | null {
194
- return this.get<T>(key) as T | null;
195
- }
231
+ offsetGet<T = unknown>(key: string): T | null {
232
+ return this.get<T>(key) as T | null;
233
+ }
196
234
 
197
- offsetSet(key: string, value: unknown): void {
198
- this.set(key, value);
199
- }
235
+ offsetSet(key: string, value: unknown): void {
236
+ this.set(key, value);
237
+ }
200
238
 
201
- offsetUnset(key: string): void {
202
- dataForget(this.items, key);
203
- }
239
+ offsetUnset(key: string): void {
240
+ dataForget(this.items, key);
241
+ }
204
242
  }