zylaris 1.0.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 (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +558 -0
  3. package/Zylaris.js.png +0 -0
  4. package/examples/default/index.html +13 -0
  5. package/examples/default/package.json +23 -0
  6. package/examples/default/src/app/about/page.tsx +18 -0
  7. package/examples/default/src/app/counter/page.tsx +22 -0
  8. package/examples/default/src/app/global.css +225 -0
  9. package/examples/default/src/app/layout.tsx +33 -0
  10. package/examples/default/src/app/page.tsx +14 -0
  11. package/examples/default/src/entry-client.tsx +87 -0
  12. package/examples/default/src/entry-server.tsx +52 -0
  13. package/examples/default/src/router.ts +60 -0
  14. package/examples/default/tsconfig.json +28 -0
  15. package/examples/default/zylaris.config.ts +24 -0
  16. package/package.json +34 -0
  17. package/packages/adapter/package.json +59 -0
  18. package/packages/adapter/src/adapters/bun.ts +215 -0
  19. package/packages/adapter/src/adapters/cloudflare.ts +278 -0
  20. package/packages/adapter/src/adapters/deno.ts +219 -0
  21. package/packages/adapter/src/adapters/netlify.ts +274 -0
  22. package/packages/adapter/src/adapters/node.ts +155 -0
  23. package/packages/adapter/src/adapters/static.ts +134 -0
  24. package/packages/adapter/src/adapters/vercel.ts +239 -0
  25. package/packages/adapter/src/index.ts +115 -0
  26. package/packages/adapter/src/lib/builder.ts +361 -0
  27. package/packages/adapter/src/types.ts +191 -0
  28. package/packages/adapter/tsconfig.json +8 -0
  29. package/packages/cli/package.json +43 -0
  30. package/packages/cli/src/bin.ts +107 -0
  31. package/packages/cli/src/commands/build.ts +197 -0
  32. package/packages/cli/src/commands/create.ts +222 -0
  33. package/packages/cli/src/commands/deploy.ts +90 -0
  34. package/packages/cli/src/commands/dev.ts +108 -0
  35. package/packages/cli/src/index.ts +6 -0
  36. package/packages/cli/tsconfig.json +9 -0
  37. package/packages/compiler/package.json +39 -0
  38. package/packages/compiler/src/index.ts +210 -0
  39. package/packages/compiler/src/jit.ts +187 -0
  40. package/packages/compiler/tsconfig.json +9 -0
  41. package/packages/core/package.json +55 -0
  42. package/packages/core/src/components.test.ts +125 -0
  43. package/packages/core/src/components.ts +181 -0
  44. package/packages/core/src/config.ts +204 -0
  45. package/packages/core/src/hooks.ts +142 -0
  46. package/packages/core/src/index.ts +59 -0
  47. package/packages/core/src/jsx-runtime.ts +46 -0
  48. package/packages/core/tsconfig.json +16 -0
  49. package/packages/dev-server/package.json +51 -0
  50. package/packages/dev-server/src/index.ts +306 -0
  51. package/packages/dev-server/src/jit-middleware.ts +78 -0
  52. package/packages/dev-server/tsconfig.json +9 -0
  53. package/packages/plugins/package.json +44 -0
  54. package/packages/plugins/src/cdn/loader.ts +275 -0
  55. package/packages/plugins/src/index.ts +238 -0
  56. package/packages/plugins/src/loaders/auto-import.ts +219 -0
  57. package/packages/plugins/src/loaders/external.ts +332 -0
  58. package/packages/plugins/src/transforms/index.ts +407 -0
  59. package/packages/plugins/src/types.ts +296 -0
  60. package/packages/plugins/tsconfig.json +8 -0
  61. package/packages/reactivity/package.json +36 -0
  62. package/packages/reactivity/src/computed.d.ts +3 -0
  63. package/packages/reactivity/src/computed.d.ts.map +1 -0
  64. package/packages/reactivity/src/computed.js +64 -0
  65. package/packages/reactivity/src/computed.js.map +1 -0
  66. package/packages/reactivity/src/computed.test.ts +83 -0
  67. package/packages/reactivity/src/computed.ts +69 -0
  68. package/packages/reactivity/src/index.d.ts +6 -0
  69. package/packages/reactivity/src/index.d.ts.map +1 -0
  70. package/packages/reactivity/src/index.js +7 -0
  71. package/packages/reactivity/src/index.js.map +1 -0
  72. package/packages/reactivity/src/index.ts +18 -0
  73. package/packages/reactivity/src/resource.d.ts +6 -0
  74. package/packages/reactivity/src/resource.d.ts.map +1 -0
  75. package/packages/reactivity/src/resource.js +43 -0
  76. package/packages/reactivity/src/resource.js.map +1 -0
  77. package/packages/reactivity/src/resource.test.ts +70 -0
  78. package/packages/reactivity/src/resource.ts +59 -0
  79. package/packages/reactivity/src/signal.d.ts +7 -0
  80. package/packages/reactivity/src/signal.d.ts.map +1 -0
  81. package/packages/reactivity/src/signal.js +145 -0
  82. package/packages/reactivity/src/signal.js.map +1 -0
  83. package/packages/reactivity/src/signal.test.ts +130 -0
  84. package/packages/reactivity/src/signal.ts +207 -0
  85. package/packages/reactivity/src/store.d.ts +4 -0
  86. package/packages/reactivity/src/store.d.ts.map +1 -0
  87. package/packages/reactivity/src/store.js +62 -0
  88. package/packages/reactivity/src/store.js.map +1 -0
  89. package/packages/reactivity/src/store.test.ts +38 -0
  90. package/packages/reactivity/src/store.ts +111 -0
  91. package/packages/reactivity/src/types.d.ts +43 -0
  92. package/packages/reactivity/src/types.d.ts.map +1 -0
  93. package/packages/reactivity/src/types.js +3 -0
  94. package/packages/reactivity/src/types.js.map +1 -0
  95. package/packages/reactivity/src/types.ts +43 -0
  96. package/packages/reactivity/tsconfig.json +9 -0
  97. package/packages/router/package.json +44 -0
  98. package/packages/router/src/components.tsx +150 -0
  99. package/packages/router/src/fs-router.ts +163 -0
  100. package/packages/router/src/index.ts +22 -0
  101. package/packages/router/src/router.test.ts +111 -0
  102. package/packages/router/src/router.ts +112 -0
  103. package/packages/router/src/types.ts +69 -0
  104. package/packages/router/tsconfig.json +10 -0
  105. package/packages/server/package.json +41 -0
  106. package/packages/server/src/action.test.ts +102 -0
  107. package/packages/server/src/action.ts +201 -0
  108. package/packages/server/src/api.ts +143 -0
  109. package/packages/server/src/index.ts +18 -0
  110. package/packages/server/src/types.ts +72 -0
  111. package/packages/server/tsconfig.json +9 -0
  112. package/pnpm-workspace.yaml +4 -0
  113. package/scripts/publish.ps1 +138 -0
  114. package/scripts/publish.sh +142 -0
  115. package/tsconfig.json +28 -0
  116. package/turbo.json +24 -0
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@zylaris/reactivity",
3
+ "version": "1.0.0",
4
+ "description": "Fine-grained reactivity system for Zylaris",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "dev": "tsc --watch",
21
+ "test": "vitest run --passWithNoTests",
22
+ "test:watch": "vitest",
23
+ "typecheck": "tsc --noEmit",
24
+ "clean": "rm -rf dist"
25
+ },
26
+ "devDependencies": {
27
+ "typescript": "^5.3.3",
28
+ "vitest": "^1.2.0"
29
+ },
30
+ "keywords": [
31
+ "reactivity",
32
+ "signals",
33
+ "zylaris"
34
+ ],
35
+ "license": "MIT"
36
+ }
@@ -0,0 +1,3 @@
1
+ import type { Computed } from './types.js';
2
+ export declare function computed<T>(fn: () => T): Computed<T>;
3
+ //# sourceMappingURL=computed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed.d.ts","sourceRoot":"","sources":["computed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAsB,MAAM,YAAY,CAAC;AAM/D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAmEpD"}
@@ -0,0 +1,64 @@
1
+ import { createEffect } from './signal.js';
2
+ // Global context for computed
3
+ let currentEffect = null;
4
+ export function computed(fn) {
5
+ const node = {
6
+ value: undefined,
7
+ observers: new Set(),
8
+ version: -1, // -1 means not computed yet
9
+ };
10
+ let dirty = true;
11
+ function compute() {
12
+ if (!dirty && node.version !== -1) {
13
+ return node.value;
14
+ }
15
+ // Track dependencies
16
+ const effect = {
17
+ fn: () => { },
18
+ dependencies: new Set(),
19
+ version: 0,
20
+ scheduled: false,
21
+ };
22
+ currentEffect = effect;
23
+ try {
24
+ node.value = fn();
25
+ node.version++;
26
+ dirty = false;
27
+ // Subscribe to all dependencies
28
+ for (const dep of effect.dependencies) {
29
+ dep.observers.add(effect);
30
+ }
31
+ // Override effect's behavior to mark dirty
32
+ effect.fn = () => {
33
+ if (!dirty) {
34
+ dirty = true;
35
+ // Notify observers
36
+ for (const observer of node.observers) {
37
+ observer.scheduled = true;
38
+ }
39
+ }
40
+ };
41
+ return node.value;
42
+ }
43
+ finally {
44
+ currentEffect = null;
45
+ }
46
+ }
47
+ function read() {
48
+ const value = compute();
49
+ if (currentEffect) {
50
+ node.observers.add(currentEffect);
51
+ currentEffect.dependencies.add(node);
52
+ }
53
+ return value;
54
+ }
55
+ return Object.assign(read, {
56
+ set: () => { throw new Error('Cannot set computed signal'); },
57
+ update: () => { throw new Error('Cannot update computed signal'); },
58
+ peek: () => node.value,
59
+ subscribe: (fn) => {
60
+ return createEffect(() => fn(read()));
61
+ },
62
+ });
63
+ }
64
+ //# sourceMappingURL=computed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed.js","sourceRoot":"","sources":["computed.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,8BAA8B;AAC9B,IAAI,aAAa,GAAkB,IAAI,CAAC;AAExC,MAAM,UAAU,QAAQ,CAAI,EAAW;IACrC,MAAM,IAAI,GAAkB;QAC1B,KAAK,EAAE,SAAc;QACrB,SAAS,EAAE,IAAI,GAAG,EAAE;QACpB,OAAO,EAAE,CAAC,CAAC,EAAE,4BAA4B;KAC1C,CAAC;IAEF,IAAI,KAAK,GAAG,IAAI,CAAC;IAEjB,SAAS,OAAO;QACd,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC;QAED,qBAAqB;QACrB,MAAM,MAAM,GAAW;YACrB,EAAE,EAAE,GAAG,EAAE,GAAE,CAAC;YACZ,YAAY,EAAE,IAAI,GAAG,EAAE;YACvB,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,KAAK;SACjB,CAAC;QACF,aAAa,GAAG,MAAM,CAAC;QAEvB,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,GAAG,EAAE,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,KAAK,GAAG,KAAK,CAAC;YAEd,gCAAgC;YAChC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBACtC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;YAED,2CAA2C;YAC3C,MAAM,CAAC,EAAE,GAAG,GAAG,EAAE;gBACf,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,KAAK,GAAG,IAAI,CAAC;oBACb,mBAAmB;oBACnB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;wBACtC,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC;oBAC5B,CAAC;gBACH,CAAC;YACH,CAAC,CAAC;YAEF,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;IACH,CAAC;IAED,SAAS,IAAI;QACX,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;QACxB,IAAI,aAAa,EAAE,CAAC;YAClB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAClC,aAAa,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QACzB,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;QAC7D,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK;QACtB,SAAS,EAAE,CAAC,EAAsB,EAAE,EAAE;YACpC,OAAO,YAAY,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { signal, computed } from './index.js';
3
+
4
+ // Helper to flush pending effects
5
+ const flushSync = () => {
6
+ // Wait for microtask queue
7
+ return new Promise(resolve => setTimeout(resolve, 0));
8
+ };
9
+
10
+ describe('computed', () => {
11
+ it('should compute derived value', () => {
12
+ const s = signal(10);
13
+ const c = computed(() => s() * 2);
14
+
15
+ expect(c()).toBe(20);
16
+ });
17
+
18
+ it('should update when dependency changes', async () => {
19
+ const s = signal(10);
20
+ const c = computed(() => s() * 2);
21
+
22
+ expect(c()).toBe(20);
23
+ s.set(20);
24
+ await flushSync(); // Wait for async update
25
+ expect(c()).toBe(40); // Should recompute when accessed
26
+ });
27
+
28
+ it('should cache computed value', () => {
29
+ const s = signal(10);
30
+ const computeFn = vi.fn(() => s() * 2);
31
+ const c = computed(computeFn);
32
+
33
+ // Access multiple times
34
+ c();
35
+ c();
36
+ c();
37
+
38
+ expect(computeFn).toHaveBeenCalledTimes(1);
39
+ });
40
+
41
+ it('should throw on set', () => {
42
+ const c = computed(() => 10);
43
+ expect(() => c.set(20)).toThrow('Cannot set computed signal');
44
+ });
45
+
46
+ it('should throw on update', () => {
47
+ const c = computed(() => 10);
48
+ expect(() => c.update(v => v * 2)).toThrow('Cannot update computed signal');
49
+ });
50
+
51
+ it('should subscribe to changes', async () => {
52
+ const s = signal(10);
53
+ const c = computed(() => s() * 2);
54
+ const fn = vi.fn();
55
+
56
+ c.subscribe(fn);
57
+ await flushSync();
58
+
59
+ expect(fn).toHaveBeenCalledWith(20); // Initial value
60
+
61
+ fn.mockClear();
62
+ s.set(20);
63
+ await flushSync();
64
+
65
+ expect(fn).toHaveBeenCalledWith(40); // New value
66
+ });
67
+
68
+ it('should handle multiple dependencies', async () => {
69
+ const s1 = signal(10);
70
+ const s2 = signal(5);
71
+ const c = computed(() => s1() + s2());
72
+
73
+ expect(c()).toBe(15);
74
+
75
+ s1.set(20);
76
+ await flushSync();
77
+ expect(c()).toBe(25); // 20 + 5
78
+
79
+ s2.set(10);
80
+ await flushSync();
81
+ expect(c()).toBe(30); // 20 + 10
82
+ });
83
+ });
@@ -0,0 +1,69 @@
1
+ import type { Computed, SignalNode } from './types.js';
2
+
3
+ // Global context for computed tracking
4
+ let currentComputed: { deps: Set<SignalNode<unknown>>; markDirty: () => void } | null = null;
5
+
6
+ export function computed<T>(fn: () => T): Computed<T> {
7
+ const node: SignalNode<T> = {
8
+ value: undefined as T,
9
+ observers: new Set(),
10
+ version: 0,
11
+ };
12
+
13
+ let dirty = true;
14
+
15
+ const markDirty = () => {
16
+ if (!dirty) {
17
+ dirty = true;
18
+ // Notify observers
19
+ for (const observer of node.observers) {
20
+ observer.scheduled = true;
21
+ }
22
+ }
23
+ };
24
+
25
+ function compute(): T {
26
+ // Cleanup and re-track dependencies
27
+ const prevComputed = currentComputed;
28
+ const deps = new Set<SignalNode<unknown>>();
29
+
30
+ currentComputed = { deps, markDirty };
31
+
32
+ try {
33
+ node.value = fn();
34
+ node.version++;
35
+ dirty = false;
36
+
37
+ // Subscribe to all dependencies
38
+ for (const dep of deps) {
39
+ dep.observers.add({ fn: markDirty } as any);
40
+ }
41
+
42
+ return node.value;
43
+ } finally {
44
+ currentComputed = prevComputed;
45
+ }
46
+ }
47
+
48
+ function read(): T {
49
+ if (dirty || node.version === 0) {
50
+ return compute();
51
+ }
52
+ return node.value;
53
+ }
54
+
55
+ return Object.assign(read, {
56
+ set: () => { throw new Error('Cannot set computed signal'); },
57
+ update: () => { throw new Error('Cannot update computed signal'); },
58
+ peek: () => node.value,
59
+ subscribe: (fn: (value: T) => void) => {
60
+ fn(read());
61
+ return () => {};
62
+ },
63
+ });
64
+ }
65
+
66
+ // Export for signal.ts to use
67
+ export function getCurrentComputed() {
68
+ return currentComputed;
69
+ }
@@ -0,0 +1,6 @@
1
+ export { signal, createEffect, batch, onMount, onCleanup } from './signal.js';
2
+ export { computed } from './computed.js';
3
+ export { resource } from './resource.js';
4
+ export { createStore, isStore } from './store.js';
5
+ export type { Signal, Computed, Resource, ResourceState, Store, EffectFn, Effect, SignalNode, } from './types.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EACV,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,GACX,MAAM,YAAY,CAAC"}
@@ -0,0 +1,7 @@
1
+ // Zylaris Reactivity System
2
+ // Fine-grained signals for optimal performance
3
+ export { signal, createEffect, batch, onMount, onCleanup } from './signal.js';
4
+ export { computed } from './computed.js';
5
+ export { resource } from './resource.js';
6
+ export { createStore, isStore } from './store.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,+CAA+C;AAE/C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,18 @@
1
+ // Zylaris Reactivity System
2
+ // Fine-grained signals for optimal performance
3
+
4
+ export { signal, createEffect, batch, onMount, onCleanup, flushSync } from './signal.js';
5
+ export { computed } from './computed.js';
6
+ export { resource } from './resource.js';
7
+ export { createStore, isStore } from './store.js';
8
+
9
+ export type {
10
+ Signal,
11
+ Computed,
12
+ Resource,
13
+ ResourceState,
14
+ Store,
15
+ EffectFn,
16
+ Effect,
17
+ SignalNode,
18
+ } from './types.js';
@@ -0,0 +1,6 @@
1
+ import type { Resource } from './types.js';
2
+ export declare function resource<T>(fetcher: () => Promise<T>, options?: {
3
+ initialValue?: T;
4
+ onError?: (error: Error) => void;
5
+ }): Resource<T>;
6
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,YAAY,CAAC;AAG1D,wBAAgB,QAAQ,CAAC,CAAC,EACxB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,OAAO,CAAC,EAAE;IACR,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,GACA,QAAQ,CAAC,CAAC,CAAC,CAgDb"}
@@ -0,0 +1,43 @@
1
+ import { signal } from './signal.js';
2
+ export function resource(fetcher, options) {
3
+ const state = signal({
4
+ status: 'pending',
5
+ ...(options?.initialValue !== undefined && {
6
+ data: options.initialValue,
7
+ }),
8
+ });
9
+ let currentPromise = null;
10
+ async function load() {
11
+ // Cancel previous request (in real implementation, use AbortController)
12
+ if (currentPromise) {
13
+ // Abort previous
14
+ }
15
+ state.set({ status: 'pending', data: undefined });
16
+ const promise = fetcher()
17
+ .then((data) => {
18
+ state.set({ status: 'success', data });
19
+ })
20
+ .catch((error) => {
21
+ state.set({ status: 'error', error });
22
+ options?.onError?.(error);
23
+ });
24
+ currentPromise = promise;
25
+ await promise;
26
+ }
27
+ // Auto-load on creation
28
+ load();
29
+ function read() {
30
+ return state();
31
+ }
32
+ return Object.assign(read, {
33
+ set: state.set,
34
+ update: state.update,
35
+ peek: state.peek,
36
+ subscribe: state.subscribe,
37
+ refetch: load,
38
+ mutate: (data) => {
39
+ state.set({ status: 'success', data });
40
+ },
41
+ });
42
+ }
43
+ //# sourceMappingURL=resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["resource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,UAAU,QAAQ,CACtB,OAAyB,EACzB,OAGC;IAED,MAAM,KAAK,GAAG,MAAM,CAAmB;QACrC,MAAM,EAAE,SAAS;QACjB,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,SAAS,IAAI;YACzC,IAAI,EAAE,OAAO,CAAC,YAAY;SAC3B,CAAC;KACiB,CAAC,CAAC;IAEvB,IAAI,cAAc,GAAyB,IAAI,CAAC;IAEhD,KAAK,UAAU,IAAI;QACjB,wEAAwE;QACxE,IAAI,cAAc,EAAE,CAAC;YACnB,iBAAiB;QACnB,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAsB,CAAC,CAAC;QAEtE,MAAM,OAAO,GAAG,OAAO,EAAE;aACtB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACb,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACtC,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEL,cAAc,GAAG,OAAO,CAAC;QACzB,MAAM,OAAO,CAAC;IAChB,CAAC;IAED,wBAAwB;IACxB,IAAI,EAAE,CAAC;IAEP,SAAS,IAAI;QACX,OAAO,KAAK,EAAE,CAAC;IACjB,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QACzB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,CAAC,IAAO,EAAE,EAAE;YAClB,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { resource } from './resource.js';
3
+
4
+ describe('resource', () => {
5
+ it('should create resource with initial state', () => {
6
+ const fetcher = vi.fn(() => Promise.resolve('data'));
7
+ const r = resource(fetcher);
8
+
9
+ const state = r();
10
+ expect(state.status).toBe('pending');
11
+ });
12
+
13
+ it('should resolve with success state', async () => {
14
+ const fetcher = vi.fn(() => Promise.resolve('data'));
15
+ const r = resource(fetcher);
16
+
17
+ // Wait for promise to resolve
18
+ await new Promise(resolve => setTimeout(resolve, 50));
19
+
20
+ const state = r();
21
+ expect(state.status).toBe('success');
22
+ expect(state.data).toBe('data');
23
+ });
24
+
25
+ it('should handle error state', async () => {
26
+ const error = new Error('fetch error');
27
+ const fetcher = vi.fn(() => Promise.reject(error));
28
+ const onError = vi.fn();
29
+
30
+ const r = resource(fetcher, { onError });
31
+
32
+ await new Promise(resolve => setTimeout(resolve, 50));
33
+
34
+ const state = r();
35
+ expect(state.status).toBe('error');
36
+ expect(state.error).toBe(error);
37
+ expect(onError).toHaveBeenCalledWith(error);
38
+ });
39
+
40
+ it('should refetch data', async () => {
41
+ let callCount = 0;
42
+ const fetcher = vi.fn(() => {
43
+ callCount++;
44
+ return Promise.resolve(`data-${callCount}`);
45
+ });
46
+
47
+ const r = resource(fetcher);
48
+ await new Promise(resolve => setTimeout(resolve, 50));
49
+
50
+ expect(r().data).toBe('data-1');
51
+
52
+ await r.refetch();
53
+ await new Promise(resolve => setTimeout(resolve, 50));
54
+
55
+ expect(r().data).toBe('data-2');
56
+ });
57
+
58
+ it('should mutate data', async () => {
59
+ const fetcher = vi.fn(() => Promise.resolve('data'));
60
+ const r = resource(fetcher);
61
+
62
+ await new Promise(resolve => setTimeout(resolve, 50));
63
+
64
+ r.mutate('mutated');
65
+
66
+ const state = r();
67
+ expect(state.status).toBe('success');
68
+ expect(state.data).toBe('mutated');
69
+ });
70
+ });
@@ -0,0 +1,59 @@
1
+ import type { Resource, ResourceState } from './types.js';
2
+ import { signal } from './signal.js';
3
+
4
+ export function resource<T>(
5
+ fetcher: () => Promise<T>,
6
+ options?: {
7
+ initialValue?: T;
8
+ onError?: (error: Error) => void;
9
+ }
10
+ ): Resource<T> {
11
+ const initialValue = options?.initialValue;
12
+ const state = signal<ResourceState<T>>({
13
+ status: 'pending',
14
+ ...(initialValue !== undefined && {
15
+ data: initialValue,
16
+ }),
17
+ } as ResourceState<T>);
18
+
19
+ let currentPromise: Promise<void> | null = null;
20
+
21
+ async function load(): Promise<void> {
22
+ // Cancel previous request (in real implementation, use AbortController)
23
+ if (currentPromise) {
24
+ // Abort previous
25
+ }
26
+
27
+ state.set({ status: 'pending', data: initialValue } as ResourceState<T>);
28
+
29
+ const promise = fetcher()
30
+ .then((data) => {
31
+ state.set({ status: 'success', data });
32
+ })
33
+ .catch((error) => {
34
+ state.set({ status: 'error', error });
35
+ options?.onError?.(error);
36
+ });
37
+
38
+ currentPromise = promise;
39
+ await promise;
40
+ }
41
+
42
+ // Auto-load on creation
43
+ load();
44
+
45
+ function read() {
46
+ return state();
47
+ }
48
+
49
+ return Object.assign(read, {
50
+ set: state.set,
51
+ update: state.update,
52
+ peek: state.peek,
53
+ subscribe: state.subscribe,
54
+ refetch: load,
55
+ mutate: (data: T) => {
56
+ state.set({ status: 'success', data });
57
+ },
58
+ });
59
+ }
@@ -0,0 +1,7 @@
1
+ import type { Signal, EffectFn } from './types.js';
2
+ export declare function signal<T>(initialValue: T): Signal<T>;
3
+ export declare function createEffect(fn: EffectFn): () => void;
4
+ export declare function batch<T>(fn: () => T): T;
5
+ export declare function onMount(fn: () => void | (() => void)): void;
6
+ export declare function onCleanup(fn: () => void): void;
7
+ //# sourceMappingURL=signal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAsB,QAAQ,EAAE,MAAM,YAAY,CAAC;AAUvE,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAoCpD;AASD,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,IAAI,CAYrD;AAoED,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAYvC;AAGD,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAQ3D;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAQ9C"}
@@ -0,0 +1,145 @@
1
+ // Global context for tracking dependencies
2
+ let currentEffect = null;
3
+ const effectStack = [];
4
+ // Effect scheduling queue
5
+ const effectQueue = [];
6
+ let isFlushing = false;
7
+ export function signal(initialValue) {
8
+ const node = {
9
+ value: initialValue,
10
+ observers: new Set(),
11
+ version: 0,
12
+ };
13
+ function read() {
14
+ // Track dependency if inside an effect
15
+ if (currentEffect) {
16
+ node.observers.add(currentEffect);
17
+ currentEffect.dependencies.add(node);
18
+ }
19
+ return node.value;
20
+ }
21
+ function set(value) {
22
+ if (Object.is(node.value, value))
23
+ return;
24
+ node.value = value;
25
+ node.version++;
26
+ notifyObservers(node);
27
+ }
28
+ function update(fn) {
29
+ set(fn(node.value));
30
+ }
31
+ function peek() {
32
+ return node.value;
33
+ }
34
+ function subscribe(fn) {
35
+ return createEffect(() => fn(read()));
36
+ }
37
+ return Object.assign(read, { set, update, peek, subscribe });
38
+ }
39
+ function notifyObservers(node) {
40
+ const observers = Array.from(node.observers);
41
+ for (const effect of observers) {
42
+ scheduleEffect(effect);
43
+ }
44
+ }
45
+ export function createEffect(fn) {
46
+ const effect = {
47
+ fn,
48
+ dependencies: new Set(),
49
+ version: 0,
50
+ scheduled: false,
51
+ };
52
+ // Initial run
53
+ runEffect(effect);
54
+ return () => disposeEffect(effect);
55
+ }
56
+ function runEffect(effect) {
57
+ // Cleanup previous dependencies
58
+ cleanupEffect(effect);
59
+ // Push to stack
60
+ effectStack.push(effect);
61
+ currentEffect = effect;
62
+ try {
63
+ const cleanup = effect.fn();
64
+ if (typeof cleanup === 'function') {
65
+ effect.cleanup = cleanup;
66
+ }
67
+ }
68
+ finally {
69
+ // Pop from stack
70
+ effectStack.pop();
71
+ currentEffect = effectStack[effectStack.length - 1] || null;
72
+ }
73
+ }
74
+ function cleanupEffect(effect) {
75
+ // Remove this effect from all signal observers
76
+ for (const dep of effect.dependencies) {
77
+ dep.observers.delete(effect);
78
+ }
79
+ effect.dependencies.clear();
80
+ // Run cleanup function if exists
81
+ if (effect.cleanup) {
82
+ effect.cleanup();
83
+ effect.cleanup = undefined;
84
+ }
85
+ }
86
+ function disposeEffect(effect) {
87
+ cleanupEffect(effect);
88
+ // Remove from queue if scheduled
89
+ const index = effectQueue.indexOf(effect);
90
+ if (index !== -1) {
91
+ effectQueue.splice(index, 1);
92
+ }
93
+ }
94
+ function scheduleEffect(effect) {
95
+ if (effect.scheduled)
96
+ return;
97
+ effect.scheduled = true;
98
+ effectQueue.push(effect);
99
+ if (!isFlushing) {
100
+ isFlushing = true;
101
+ queueMicrotask(flushEffects);
102
+ }
103
+ }
104
+ function flushEffects() {
105
+ isFlushing = true;
106
+ while (effectQueue.length > 0) {
107
+ const effect = effectQueue.shift();
108
+ effect.scheduled = false;
109
+ runEffect(effect);
110
+ }
111
+ isFlushing = false;
112
+ }
113
+ export function batch(fn) {
114
+ const prevFlushing = isFlushing;
115
+ isFlushing = true;
116
+ try {
117
+ return fn();
118
+ }
119
+ finally {
120
+ isFlushing = prevFlushing;
121
+ if (!isFlushing) {
122
+ flushEffects();
123
+ }
124
+ }
125
+ }
126
+ // Lifecycle hooks
127
+ export function onMount(fn) {
128
+ if (typeof window !== 'undefined') {
129
+ createEffect(() => {
130
+ fn();
131
+ // Only run once
132
+ return () => { };
133
+ });
134
+ }
135
+ }
136
+ export function onCleanup(fn) {
137
+ if (currentEffect) {
138
+ const prevCleanup = currentEffect.cleanup;
139
+ currentEffect.cleanup = () => {
140
+ prevCleanup?.();
141
+ fn();
142
+ };
143
+ }
144
+ }
145
+ //# sourceMappingURL=signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.js","sourceRoot":"","sources":["signal.ts"],"names":[],"mappings":"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"}