@gencow/core 0.1.24 → 0.1.26

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 (75) hide show
  1. package/dist/crud.d.ts +2 -2
  2. package/dist/crud.js +225 -208
  3. package/dist/index.d.ts +5 -5
  4. package/dist/index.js +2 -2
  5. package/dist/reactive.js +10 -3
  6. package/dist/retry.js +1 -1
  7. package/dist/rls-db.d.ts +2 -2
  8. package/dist/rls-db.js +1 -5
  9. package/dist/scheduler.d.ts +2 -0
  10. package/dist/scheduler.js +16 -6
  11. package/dist/server.d.ts +0 -1
  12. package/dist/server.js +0 -1
  13. package/dist/storage.js +29 -22
  14. package/dist/v.d.ts +2 -2
  15. package/dist/workflow.js +4 -11
  16. package/dist/workflows-api.js +5 -12
  17. package/package.json +45 -42
  18. package/src/__tests__/auth.test.ts +90 -86
  19. package/src/__tests__/crons.test.ts +69 -67
  20. package/src/__tests__/crud-codegen-integration.test.ts +164 -170
  21. package/src/__tests__/crud-owner-rls.test.ts +308 -301
  22. package/src/__tests__/crud.test.ts +694 -711
  23. package/src/__tests__/dist-exports.test.ts +120 -120
  24. package/src/__tests__/fixtures/basic/auth.ts +16 -16
  25. package/src/__tests__/fixtures/basic/drizzle.config.ts +1 -4
  26. package/src/__tests__/fixtures/basic/index.ts +1 -1
  27. package/src/__tests__/fixtures/basic/schema.ts +1 -1
  28. package/src/__tests__/fixtures/basic/tasks.ts +4 -4
  29. package/src/__tests__/fixtures/common/auth-schema.ts +38 -34
  30. package/src/__tests__/helpers/basic-rls-fixture.ts +80 -78
  31. package/src/__tests__/helpers/pglite-migrations.ts +2 -5
  32. package/src/__tests__/helpers/pglite-rls-session.ts +13 -16
  33. package/src/__tests__/helpers/seed-like-fill.ts +47 -41
  34. package/src/__tests__/helpers/test-gencow-ctx-rls.ts +4 -7
  35. package/src/__tests__/httpaction.test.ts +91 -91
  36. package/src/__tests__/image-optimization.test.ts +570 -574
  37. package/src/__tests__/load.test.ts +321 -308
  38. package/src/__tests__/network-sim.test.ts +238 -215
  39. package/src/__tests__/reactive.test.ts +380 -358
  40. package/src/__tests__/retry.test.ts +99 -84
  41. package/src/__tests__/rls-crud-basic.test.ts +172 -245
  42. package/src/__tests__/rls-crud-no-owner-rls-pglite.test.ts +81 -81
  43. package/src/__tests__/rls-custom-mutation-handlers.test.ts +47 -94
  44. package/src/__tests__/rls-custom-query-handlers.test.ts +92 -92
  45. package/src/__tests__/rls-db-leased-connection.test.ts +2 -6
  46. package/src/__tests__/rls-session-and-policies.test.ts +181 -199
  47. package/src/__tests__/scheduler-durable-v2.test.ts +199 -181
  48. package/src/__tests__/scheduler-durable.test.ts +117 -117
  49. package/src/__tests__/scheduler-exec.test.ts +258 -246
  50. package/src/__tests__/scheduler.test.ts +129 -111
  51. package/src/__tests__/storage.test.ts +282 -269
  52. package/src/__tests__/tsconfig.json +6 -6
  53. package/src/__tests__/validator.test.ts +236 -232
  54. package/src/__tests__/workflow.test.ts +309 -286
  55. package/src/__tests__/ws-integration.test.ts +223 -218
  56. package/src/__tests__/ws-scale.test.ts +168 -159
  57. package/src/auth-config.ts +18 -18
  58. package/src/auth.ts +106 -106
  59. package/src/crons.ts +77 -77
  60. package/src/crud.ts +523 -479
  61. package/src/index.ts +69 -5
  62. package/src/reactive.ts +357 -331
  63. package/src/retry.ts +51 -54
  64. package/src/rls-db.ts +195 -205
  65. package/src/rls.ts +33 -36
  66. package/src/scheduler.ts +237 -211
  67. package/src/server.ts +0 -1
  68. package/src/storage.ts +632 -593
  69. package/src/v.ts +119 -114
  70. package/src/workflow-types.ts +67 -70
  71. package/src/workflow.ts +99 -116
  72. package/src/workflows-api.ts +231 -241
  73. package/dist/db.d.ts +0 -13
  74. package/dist/db.js +0 -16
  75. package/src/db.ts +0 -18
@@ -10,160 +10,178 @@ import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test";
10
10
  import { createScheduler, getSchedulerInfo } from "../scheduler";
11
11
 
12
12
  describe("createScheduler()", () => {
13
- let scheduler: ReturnType<typeof createScheduler>;
13
+ let scheduler: ReturnType<typeof createScheduler>;
14
14
 
15
- beforeEach(() => {
16
- scheduler = createScheduler();
15
+ beforeEach(() => {
16
+ scheduler = createScheduler();
17
+ });
18
+
19
+ // ─── registerAction + executeAction ──────────────────
20
+
21
+ describe("registerAction / executeAction", () => {
22
+ it("등록된 액션 실행", async () => {
23
+ const fn = mock(async (args: any) => {});
24
+ scheduler.registerAction("test.action", fn);
25
+
26
+ await scheduler.executeAction("test.action", { foo: 1 });
27
+
28
+ expect(fn).toHaveBeenCalledTimes(1);
29
+ expect(fn.mock.calls[0][0]).toEqual({ foo: 1 });
17
30
  });
18
31
 
19
- // ─── registerAction + executeAction ──────────────────
32
+ it("미등록 액션 실행 console.error만 출력 (throw 안 함)", async () => {
33
+ const errorSpy = mock(() => {});
34
+ const orig = console.error;
35
+ console.error = errorSpy;
20
36
 
21
- describe("registerAction / executeAction", () => {
22
- it("등록된 액션 실행", async () => {
23
- const fn = mock(async (args: any) => {});
24
- scheduler.registerAction("test.action", fn);
37
+ await scheduler.executeAction("nonexistent.action");
25
38
 
26
- await scheduler.executeAction("test.action", { foo: 1 });
39
+ expect(errorSpy).toHaveBeenCalled();
40
+ expect(String(errorSpy.mock.calls[0][0])).toContain("nonexistent.action");
27
41
 
28
- expect(fn).toHaveBeenCalledTimes(1);
29
- expect(fn.mock.calls[0][0]).toEqual({ foo: 1 });
30
- });
42
+ console.error = orig;
43
+ });
44
+
45
+ it("액션 핸들러 에러 시 console.error 출력 (throw 안 함)", async () => {
46
+ scheduler.registerAction("failing.action", async () => {
47
+ throw new Error("action failed");
48
+ });
31
49
 
32
- it("미등록 액션 실행 시 console.error만 출력 (throw 안 함)", async () => {
33
- const errorSpy = mock(() => {});
34
- const orig = console.error;
35
- console.error = errorSpy;
50
+ const errorSpy = mock((..._args: any[]) => {});
51
+ const orig = console.error;
52
+ console.error = errorSpy;
36
53
 
37
- await scheduler.executeAction("nonexistent.action");
54
+ await scheduler.executeAction("failing.action");
38
55
 
39
- expect(errorSpy).toHaveBeenCalled();
40
- expect(String(errorSpy.mock.calls[0][0])).toContain("nonexistent.action");
56
+ expect(errorSpy).toHaveBeenCalled();
41
57
 
42
- console.error = orig;
43
- });
58
+ console.error = orig;
59
+ });
44
60
 
45
- it("액션 핸들러 에러 console.error 출력 (throw 안 함)", async () => {
46
- scheduler.registerAction("failing.action", async () => {
47
- throw new Error("action failed");
48
- });
61
+ it("executeActionStrict는 등록된 액션의 args를 그대로 전달한다", async () => {
62
+ const fn = mock(async (_args: any) => {});
63
+ scheduler.registerAction("strict.action", fn);
49
64
 
50
- const errorSpy = mock((..._args: any[]) => {});
51
- const orig = console.error;
52
- console.error = errorSpy;
65
+ await scheduler.executeActionStrict("strict.action", { workflowId: "wf-1" });
53
66
 
54
- await scheduler.executeAction("failing.action");
67
+ expect(fn).toHaveBeenCalledTimes(1);
68
+ expect(fn.mock.calls[0][0]).toEqual({ workflowId: "wf-1" });
69
+ });
55
70
 
56
- expect(errorSpy).toHaveBeenCalled();
71
+ it("executeActionStrict는 액션 에러를 호출자에게 다시 던진다", async () => {
72
+ scheduler.registerAction("strict.fail", async () => {
73
+ throw new Error("strict failed");
74
+ });
57
75
 
58
- console.error = orig;
59
- });
76
+ await expect(scheduler.executeActionStrict("strict.fail")).rejects.toThrow("strict failed");
60
77
  });
78
+ });
61
79
 
62
- // ─── runAfter ───────────────────────────────────────
80
+ // ─── runAfter ───────────────────────────────────────
63
81
 
64
- describe("runAfter()", () => {
65
- it("jobId를 반환한다", () => {
66
- const jobId = scheduler.runAfter(10000, "test.action");
67
- expect(typeof jobId).toBe("string");
68
- expect(jobId).toContain("job_");
82
+ describe("runAfter()", () => {
83
+ it("jobId를 반환한다", () => {
84
+ const jobId = scheduler.runAfter(10000, "test.action");
85
+ expect(typeof jobId).toBe("string");
86
+ expect(jobId).toContain("job_");
69
87
 
70
- // cleanup
71
- scheduler.cancel(jobId);
72
- });
88
+ // cleanup
89
+ scheduler.cancel(jobId);
90
+ });
73
91
 
74
- it("지정 시간 후 액션을 실행한다", async () => {
75
- const fn = mock(async () => {});
76
- scheduler.registerAction("delayed.action", fn);
92
+ it("지정 시간 후 액션을 실행한다", async () => {
93
+ const fn = mock(async () => {});
94
+ scheduler.registerAction("delayed.action", fn);
77
95
 
78
- scheduler.runAfter(50, "delayed.action", { key: "val" });
96
+ scheduler.runAfter(50, "delayed.action", { key: "val" });
79
97
 
80
- // 액션은 아직 실행되지 않아야 함
81
- expect(fn).not.toHaveBeenCalled();
98
+ // 액션은 아직 실행되지 않아야 함
99
+ expect(fn).not.toHaveBeenCalled();
82
100
 
83
- // 100ms 대기 후 실행됨
84
- await new Promise(r => setTimeout(r, 100));
85
- expect(fn).toHaveBeenCalledTimes(1);
86
- expect(fn.mock.calls[0][0]).toEqual({ key: "val" });
87
- });
101
+ // 100ms 대기 후 실행됨
102
+ await new Promise((r) => setTimeout(r, 100));
103
+ expect(fn).toHaveBeenCalledTimes(1);
104
+ expect(fn.mock.calls[0][0]).toEqual({ key: "val" });
105
+ });
88
106
 
89
- it("pendingJobs에 등록된다", () => {
90
- const jobId = scheduler.runAfter(60000, "test.long");
91
- const info = getSchedulerInfo();
92
- const found = info.pendingJobs.find(j => j.id === jobId);
93
- expect(found).toBeDefined();
94
- expect(found!.action).toBe("test.long");
107
+ it("pendingJobs에 등록된다", () => {
108
+ const jobId = scheduler.runAfter(60000, "test.long");
109
+ const info = getSchedulerInfo();
110
+ const found = info.pendingJobs.find((j) => j.id === jobId);
111
+ expect(found).toBeDefined();
112
+ expect(found!.action).toBe("test.long");
95
113
 
96
- // cleanup
97
- scheduler.cancel(jobId);
98
- });
114
+ // cleanup
115
+ scheduler.cancel(jobId);
99
116
  });
117
+ });
100
118
 
101
- // ─── cancel ─────────────────────────────────────────
119
+ // ─── cancel ─────────────────────────────────────────
102
120
 
103
- describe("cancel()", () => {
104
- it("예약된 작업을 취소한다", () => {
105
- const fn = mock(async () => {});
106
- scheduler.registerAction("cancel.test", fn);
121
+ describe("cancel()", () => {
122
+ it("예약된 작업을 취소한다", () => {
123
+ const fn = mock(async () => {});
124
+ scheduler.registerAction("cancel.test", fn);
107
125
 
108
- const jobId = scheduler.runAfter(50, "cancel.test");
109
- const cancelled = scheduler.cancel(jobId);
126
+ const jobId = scheduler.runAfter(50, "cancel.test");
127
+ const cancelled = scheduler.cancel(jobId);
110
128
 
111
- expect(cancelled).toBe(true);
112
- });
129
+ expect(cancelled).toBe(true);
130
+ });
113
131
 
114
- it("존재하지 않는 jobId → false 반환", () => {
115
- expect(scheduler.cancel("nonexistent_id")).toBe(false);
116
- });
132
+ it("존재하지 않는 jobId → false 반환", () => {
133
+ expect(scheduler.cancel("nonexistent_id")).toBe(false);
134
+ });
117
135
 
118
- it("취소된 작업은 실행되지 않는다", async () => {
119
- const fn = mock(async () => {});
120
- scheduler.registerAction("cancel.verify", fn);
136
+ it("취소된 작업은 실행되지 않는다", async () => {
137
+ const fn = mock(async () => {});
138
+ scheduler.registerAction("cancel.verify", fn);
121
139
 
122
- const jobId = scheduler.runAfter(30, "cancel.verify");
123
- scheduler.cancel(jobId);
140
+ const jobId = scheduler.runAfter(30, "cancel.verify");
141
+ scheduler.cancel(jobId);
124
142
 
125
- await new Promise(r => setTimeout(r, 100));
126
- expect(fn).not.toHaveBeenCalled();
127
- });
143
+ await new Promise((r) => setTimeout(r, 100));
144
+ expect(fn).not.toHaveBeenCalled();
128
145
  });
146
+ });
129
147
 
130
- // ─── runAt ──────────────────────────────────────────
148
+ // ─── runAt ──────────────────────────────────────────
131
149
 
132
- describe("runAt()", () => {
133
- it("Date 객체로 예약 가능", () => {
134
- const future = new Date(Date.now() + 60000);
135
- const jobId = scheduler.runAt(future, "test.runat");
136
- expect(typeof jobId).toBe("string");
137
- scheduler.cancel(jobId);
138
- });
150
+ describe("runAt()", () => {
151
+ it("Date 객체로 예약 가능", () => {
152
+ const future = new Date(Date.now() + 60000);
153
+ const jobId = scheduler.runAt(future, "test.runat");
154
+ expect(typeof jobId).toBe("string");
155
+ scheduler.cancel(jobId);
156
+ });
139
157
 
140
- it("timestamp(ms)로 예약 가능", () => {
141
- const ts = Date.now() + 60000;
142
- const jobId = scheduler.runAt(ts, "test.runat.ts");
143
- expect(typeof jobId).toBe("string");
144
- scheduler.cancel(jobId);
145
- });
158
+ it("timestamp(ms)로 예약 가능", () => {
159
+ const ts = Date.now() + 60000;
160
+ const jobId = scheduler.runAt(ts, "test.runat.ts");
161
+ expect(typeof jobId).toBe("string");
162
+ scheduler.cancel(jobId);
163
+ });
146
164
 
147
- it("과거 시각 → 즉시 실행 (ms=0)", async () => {
148
- const fn = mock(async () => {});
149
- scheduler.registerAction("past.action", fn);
165
+ it("과거 시각 → 즉시 실행 (ms=0)", async () => {
166
+ const fn = mock(async () => {});
167
+ scheduler.registerAction("past.action", fn);
150
168
 
151
- scheduler.runAt(Date.now() - 10000, "past.action");
169
+ scheduler.runAt(Date.now() - 10000, "past.action");
152
170
 
153
- await new Promise(r => setTimeout(r, 50));
154
- expect(fn).toHaveBeenCalledTimes(1);
155
- });
171
+ await new Promise((r) => setTimeout(r, 50));
172
+ expect(fn).toHaveBeenCalledTimes(1);
156
173
  });
174
+ });
157
175
 
158
- // ─── getSchedulerInfo ───────────────────────────────
176
+ // ─── getSchedulerInfo ───────────────────────────────
159
177
 
160
- describe("getSchedulerInfo()", () => {
161
- it("crons와 pendingJobs를 반환한다", () => {
162
- const info = getSchedulerInfo();
163
- expect(info).toHaveProperty("crons");
164
- expect(info).toHaveProperty("pendingJobs");
165
- expect(Array.isArray(info.crons)).toBe(true);
166
- expect(Array.isArray(info.pendingJobs)).toBe(true);
167
- });
178
+ describe("getSchedulerInfo()", () => {
179
+ it("crons와 pendingJobs를 반환한다", () => {
180
+ const info = getSchedulerInfo();
181
+ expect(info).toHaveProperty("crons");
182
+ expect(info).toHaveProperty("pendingJobs");
183
+ expect(Array.isArray(info.crons)).toBe(true);
184
+ expect(Array.isArray(info.pendingJobs)).toBe(true);
168
185
  });
186
+ });
169
187
  });