@sigil-dev/grimoire 0.7.5 → 0.7.6

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 (50) hide show
  1. package/.grimoire/_routes.dom.js +8 -0
  2. package/.grimoire/_routes.hydrate.js +8 -0
  3. package/.grimoire/tsconfig.generated.json +11 -0
  4. package/.grimoire/types/ambient.d.ts +59 -0
  5. package/.grimoire/types/api/hello/$types.d.ts +50 -0
  6. package/.grimoire/types/api/items/$types.d.ts +50 -0
  7. package/.grimoire/types/echo/$types.d.ts +50 -0
  8. package/.grimoire/types/env-private.d.ts +5 -0
  9. package/.grimoire/types/env-public.d.ts +5 -0
  10. package/.grimoire/types/mixed/$types.d.ts +50 -0
  11. package/.grimoire/types/params/[docId]/$types.d.ts +52 -0
  12. package/.grimoire/types/reject/$types.d.ts +50 -0
  13. package/index.ts +34 -34
  14. package/package.json +8 -4
  15. package/preload.js +2 -0
  16. package/public/__grimoire__/hydrate.js +585 -0
  17. package/public/__grimoire__/index.js +490 -0
  18. package/src/client/head.ts +29 -0
  19. package/src/client/router.ts +224 -76
  20. package/src/env/index.ts +25 -0
  21. package/src/env/plugin.ts +13 -0
  22. package/src/env/private.ts +5 -0
  23. package/src/env/public.ts +7 -0
  24. package/src/env/typegen.ts +51 -0
  25. package/src/integrations/vite.ts +72 -72
  26. package/src/rendering/head.ts +22 -2
  27. package/src/rendering/hydrate.ts +81 -27
  28. package/src/rendering/index.ts +199 -186
  29. package/src/rendering/ssrPlugin.ts +53 -47
  30. package/src/routing/manifest-gen.ts +39 -26
  31. package/src/routing/router.ts +106 -98
  32. package/src/routing/scanner.ts +135 -129
  33. package/src/routing/transform-routes.ts +101 -101
  34. package/src/server/build.ts +147 -90
  35. package/src/server/coordinator.ts +306 -297
  36. package/src/server/hooks.ts +24 -3
  37. package/src/server/index.ts +144 -70
  38. package/src/server/worker.ts +59 -59
  39. package/src/typegen/index.ts +353 -340
  40. package/src/types.ts +269 -260
  41. package/test/context.test.ts +52 -52
  42. package/test/hydration.test.ts +119 -119
  43. package/test/middleware.test.ts +223 -221
  44. package/test/rendering.test.ts +425 -425
  45. package/test/routing.test.ts +83 -45
  46. package/test/scanning.test.ts +181 -169
  47. package/test/server.test.ts +229 -229
  48. package/test/streaming.test.ts +106 -106
  49. package/test/transform-routes.test.ts +84 -84
  50. package/test/typegen.test.ts +19 -1
@@ -1,119 +1,119 @@
1
- import { Window } from "happy-dom";
2
-
3
- // set up DOM globals before tests
4
- const window = new Window();
5
- const document = window.document;
6
-
7
- // @ts-expect-error
8
- globalThis.document = document;
9
- // @ts-expect-error
10
- globalThis.window = window;
11
- // @ts-expect-error
12
- globalThis.HTMLElement = window.HTMLElement;
13
- // @ts-expect-error
14
- globalThis.Event = window.Event;
15
- // @ts-expect-error
16
- globalThis.MouseEvent = window.MouseEvent;
17
- // @ts-expect-error
18
- globalThis.SyntaxError = SyntaxError;
19
-
20
- import { beforeEach, describe, expect, test } from "bun:test";
21
-
22
- // mock routes - simulates what #grimoire-routes generates
23
- const mockRoutes: Record<string, (props: any) => any> = {
24
- "/spells/:id": (props: any) => {
25
- return { params: props.params };
26
- },
27
- };
28
-
29
- // simulate hydrate logic (same as hydrate.ts but without the import)
30
- function runHydrate(routes: Record<string, (props: any) => any>) {
31
- const stateEl = document.getElementById("__grimoire_state__");
32
- if (stateEl) {
33
- const state = JSON.parse(stateEl.textContent!);
34
- const Page = routes[state.pattern];
35
- if (Page) {
36
- Page({ ...state.data, params: state.params });
37
- }
38
- }
39
- }
40
-
41
- // simulate initRouter (same logic as router.ts)
42
- let clickHandlerAttached = false;
43
- function initRouter(routes: Record<string, (props: any) => any>) {
44
- document.addEventListener("click", () => {});
45
- clickHandlerAttached = true;
46
- }
47
-
48
- describe("hydration", () => {
49
- beforeEach(() => {
50
- clickHandlerAttached = false;
51
- document.body.innerHTML = `
52
- <div id="app">
53
- <div id="grimoire-page">
54
- <div class="spell-detail">
55
- <h1>Fireball</h1>
56
- </div>
57
- </div>
58
- </div>
59
- <script id="__grimoire_state__" type="application/json">
60
- ${JSON.stringify({ params: { id: "fireball" }, data: {}, pattern: "/spells/:id" })}
61
- </script>
62
- `;
63
- });
64
-
65
- // 1. hydrate claims existing DOM, doesn't create new nodes
66
- test("hydrate claims existing h1 instead of creating new one", () => {
67
- const before = document.getElementById("app")!.innerHTML;
68
- runHydrate(mockRoutes);
69
- // DOM unchanged - hydrate only reads state, doesn't write
70
- expect(document.getElementById("app")!.innerHTML).toBe(before);
71
- });
72
-
73
- // 2. router attaches after hydration
74
- test("initRouter attaches click handler", () => {
75
- initRouter(mockRoutes);
76
- expect(clickHandlerAttached).toBe(true);
77
- });
78
-
79
- // 3. state script is readable
80
- test("grimoire state is parseable", () => {
81
- const el = document.getElementById("__grimoire_state__");
82
- const state = JSON.parse(el!.textContent!);
83
- expect(state.pattern).toBe("/spells/:id");
84
- expect(state.params).toEqual({ id: "fireball" });
85
- });
86
-
87
- // 4. hydrate doesn't touch DOM structure
88
- test("hydrate preserves DOM structure", () => {
89
- const before = document.body.innerHTML;
90
- runHydrate(mockRoutes);
91
- expect(document.body.innerHTML).toBe(before);
92
- });
93
-
94
- // 5. hydrate with missing pattern is safe
95
- test("hydrate with unknown pattern does not throw", () => {
96
- const el = document.getElementById("__grimoire_state__");
97
- const state = JSON.parse(el!.textContent!);
98
- state.pattern = "/nonexistent";
99
- el!.textContent = JSON.stringify(state);
100
-
101
- expect(() => runHydrate(mockRoutes)).not.toThrow();
102
- });
103
-
104
- // 6. hydrate passes data and params correctly
105
- test("hydrate passes data and params to page component", () => {
106
- let receivedProps: any = null;
107
- const testRoutes: Record<string, (props: any) => any> = {
108
- "/spells/:id": (props: any) => {
109
- receivedProps = props;
110
- return null;
111
- },
112
- };
113
-
114
- runHydrate(testRoutes);
115
- expect(receivedProps).toEqual({
116
- params: { id: "fireball" },
117
- });
118
- });
119
- });
1
+ import { Window } from "happy-dom";
2
+
3
+ // set up DOM globals before tests
4
+ const window = new Window();
5
+ const document = window.document;
6
+
7
+ // @ts-expect-error
8
+ globalThis.document = document;
9
+ // @ts-expect-error
10
+ globalThis.window = window;
11
+ // @ts-expect-error
12
+ globalThis.HTMLElement = window.HTMLElement;
13
+ // @ts-expect-error
14
+ globalThis.Event = window.Event;
15
+ // @ts-expect-error
16
+ globalThis.MouseEvent = window.MouseEvent;
17
+ // @ts-expect-error
18
+ globalThis.SyntaxError = SyntaxError;
19
+
20
+ import { beforeEach, describe, expect, test } from "bun:test";
21
+
22
+ // mock routes - simulates what #grimoire-routes generates
23
+ const mockRoutes: Record<string, (props: any) => any> = {
24
+ "/spells/:id": (props: any) => {
25
+ return { params: props.params };
26
+ },
27
+ };
28
+
29
+ // simulate hydrate logic (same as hydrate.ts but without the import)
30
+ function runHydrate(routes: Record<string, (props: any) => any>) {
31
+ const stateEl = document.getElementById("__grimoire_state__");
32
+ if (stateEl) {
33
+ const state = JSON.parse(stateEl.textContent!);
34
+ const Page = routes[state.pattern];
35
+ if (Page) {
36
+ Page({ ...state.data, params: state.params });
37
+ }
38
+ }
39
+ }
40
+
41
+ // simulate initRouter (same logic as router.ts)
42
+ let clickHandlerAttached = false;
43
+ function initRouter(routes: Record<string, (props: any) => any>) {
44
+ document.addEventListener("click", () => {});
45
+ clickHandlerAttached = true;
46
+ }
47
+
48
+ describe("hydration", () => {
49
+ beforeEach(() => {
50
+ clickHandlerAttached = false;
51
+ document.body.innerHTML = `
52
+ <div id="app">
53
+ <div id="grimoire-page">
54
+ <div class="spell-detail">
55
+ <h1>Fireball</h1>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ <script id="__grimoire_state__" type="application/json">
60
+ ${JSON.stringify({ params: { id: "fireball" }, data: {}, pattern: "/spells/:id" })}
61
+ </script>
62
+ `;
63
+ });
64
+
65
+ // 1. hydrate claims existing DOM, doesn't create new nodes
66
+ test("hydrate claims existing h1 instead of creating new one", () => {
67
+ const before = document.getElementById("app")!.innerHTML;
68
+ runHydrate(mockRoutes);
69
+ // DOM unchanged - hydrate only reads state, doesn't write
70
+ expect(document.getElementById("app")!.innerHTML).toBe(before);
71
+ });
72
+
73
+ // 2. router attaches after hydration
74
+ test("initRouter attaches click handler", () => {
75
+ initRouter(mockRoutes);
76
+ expect(clickHandlerAttached).toBe(true);
77
+ });
78
+
79
+ // 3. state script is readable
80
+ test("grimoire state is parseable", () => {
81
+ const el = document.getElementById("__grimoire_state__");
82
+ const state = JSON.parse(el!.textContent!);
83
+ expect(state.pattern).toBe("/spells/:id");
84
+ expect(state.params).toEqual({ id: "fireball" });
85
+ });
86
+
87
+ // 4. hydrate doesn't touch DOM structure
88
+ test("hydrate preserves DOM structure", () => {
89
+ const before = document.body.innerHTML;
90
+ runHydrate(mockRoutes);
91
+ expect(document.body.innerHTML).toBe(before);
92
+ });
93
+
94
+ // 5. hydrate with missing pattern is safe
95
+ test("hydrate with unknown pattern does not throw", () => {
96
+ const el = document.getElementById("__grimoire_state__");
97
+ const state = JSON.parse(el!.textContent!);
98
+ state.pattern = "/nonexistent";
99
+ el!.textContent = JSON.stringify(state);
100
+
101
+ expect(() => runHydrate(mockRoutes)).not.toThrow();
102
+ });
103
+
104
+ // 6. hydrate passes data and params correctly
105
+ test("hydrate passes data and params to page component", () => {
106
+ let receivedProps: any = null;
107
+ const testRoutes: Record<string, (props: any) => any> = {
108
+ "/spells/:id": (props: any) => {
109
+ receivedProps = props;
110
+ return null;
111
+ },
112
+ };
113
+
114
+ runHydrate(testRoutes);
115
+ expect(receivedProps).toEqual({
116
+ params: { id: "fireball" },
117
+ });
118
+ });
119
+ });