@edge-base/server 0.2.4 → 0.2.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 (131) hide show
  1. package/admin-build/_app/immutable/chunks/{DILS_-VJ.js → B3CvhH3c.js} +1 -1
  2. package/admin-build/_app/immutable/chunks/BN_-k-Ck.js +1 -0
  3. package/admin-build/_app/immutable/chunks/{Dt4vL4Df.js → BYL_uBga.js} +1 -1
  4. package/admin-build/_app/immutable/chunks/{C72lTcG0.js → Bcs4KYNp.js} +1 -1
  5. package/admin-build/_app/immutable/chunks/{B8s_s9QY.js → BkZCgsc3.js} +1 -1
  6. package/admin-build/_app/immutable/chunks/{BgDzp0i0.js → BvoGcDFV.js} +1 -1
  7. package/admin-build/_app/immutable/chunks/{BME_U9TJ.js → CCUxCptE.js} +1 -1
  8. package/admin-build/_app/immutable/chunks/CLHN9MVr.js +1 -0
  9. package/admin-build/_app/immutable/chunks/{DYaCRWMA.js → CR37B8DX.js} +1 -1
  10. package/admin-build/_app/immutable/chunks/CbfX3ELZ.js +1 -0
  11. package/admin-build/_app/immutable/chunks/CjcrXziO.js +2 -0
  12. package/admin-build/_app/immutable/chunks/CrwlCAM0.js +1 -0
  13. package/admin-build/_app/immutable/chunks/{B0HRJ657.js → DOOPbWwG.js} +1 -1
  14. package/admin-build/_app/immutable/chunks/DQVP4KC-.js +1 -0
  15. package/admin-build/_app/immutable/chunks/{Dj0QUuOf.js → DdvsFblq.js} +1 -1
  16. package/admin-build/_app/immutable/chunks/DemDWbs-.js +128 -0
  17. package/admin-build/_app/immutable/chunks/{XQM1k9PM.js → DmDTovpg.js} +1 -1
  18. package/admin-build/_app/immutable/chunks/{fYEKMQ-Z.js → Ff90owjx.js} +1 -1
  19. package/admin-build/_app/immutable/chunks/{5RQRbp5q.js → LL3ulaxa.js} +1 -1
  20. package/admin-build/_app/immutable/chunks/{DBsVqhuh.js → Q3vAxeY-.js} +1 -1
  21. package/admin-build/_app/immutable/chunks/{D__dwMuW.js → SQVAC3Cv.js} +1 -1
  22. package/admin-build/_app/immutable/chunks/{Z41NK6i6.js → bguI1TeA.js} +1 -1
  23. package/admin-build/_app/immutable/chunks/{_teD5ji5.js → nlAMTi52.js} +1 -1
  24. package/admin-build/_app/immutable/chunks/{BjWZuf8W.js → qBm6xof8.js} +1 -1
  25. package/admin-build/_app/immutable/entry/{app.C8ylfBe6.js → app.CP83Ni80.js} +2 -2
  26. package/admin-build/_app/immutable/entry/start.DY6YakU0.js +1 -0
  27. package/admin-build/_app/immutable/nodes/{0.CJJ6HZbp.js → 0.DiRq7puO.js} +1 -1
  28. package/admin-build/_app/immutable/nodes/1.BFeyKLGT.js +1 -0
  29. package/admin-build/_app/immutable/nodes/10.zcee7hJx.js +1 -0
  30. package/admin-build/_app/immutable/nodes/11.BW7wLs2Y.js +1 -0
  31. package/admin-build/_app/immutable/nodes/12.CxJRlYSd.js +1 -0
  32. package/admin-build/_app/immutable/nodes/13.pp0F_5hn.js +110 -0
  33. package/admin-build/_app/immutable/nodes/14.t3AfGiGo.js +3 -0
  34. package/admin-build/_app/immutable/nodes/15.B3agc7NX.js +1 -0
  35. package/admin-build/_app/immutable/nodes/{16.D0xkPUBW.js → 16.C4uG2-i8.js} +1 -1
  36. package/admin-build/_app/immutable/nodes/{17.CebNqPeh.js → 17.CwGxi1Bn.js} +1 -1
  37. package/admin-build/_app/immutable/nodes/18.CrQyN_gU.js +1 -0
  38. package/admin-build/_app/immutable/nodes/19.NEPUOXl7.js +2 -0
  39. package/admin-build/_app/immutable/nodes/{20.DYb-q3W8.js → 20.DGHO8ipr.js} +1 -1
  40. package/admin-build/_app/immutable/nodes/21.UVKBDvp4.js +1 -0
  41. package/admin-build/_app/immutable/nodes/22.Dri5It7a.js +1 -0
  42. package/admin-build/_app/immutable/nodes/{23.BLgq21om.js → 23.BPQP_Zte.js} +2 -2
  43. package/admin-build/_app/immutable/nodes/24.D580FdSS.js +2 -0
  44. package/admin-build/_app/immutable/nodes/25.BMNPOZwF.js +2 -0
  45. package/admin-build/_app/immutable/nodes/26.XcpEcbiz.js +1 -0
  46. package/admin-build/_app/immutable/nodes/27.C1zHHcYv.js +1 -0
  47. package/admin-build/_app/immutable/nodes/28.CuKzzrY8.js +1 -0
  48. package/admin-build/_app/immutable/nodes/29.nLpBMXnM.js +1 -0
  49. package/admin-build/_app/immutable/nodes/{3.z8ut3jS-.js → 3.5G_aseoL.js} +1 -1
  50. package/admin-build/_app/immutable/nodes/30.CQC4nLoU.js +1 -0
  51. package/admin-build/_app/immutable/nodes/31.Bet8kxOK.js +1 -0
  52. package/admin-build/_app/immutable/nodes/4.nmJDYJpC.js +1 -0
  53. package/admin-build/_app/immutable/nodes/5.CnbYLG4E.js +1 -0
  54. package/admin-build/_app/immutable/nodes/6.KA01b-3y.js +1 -0
  55. package/admin-build/_app/immutable/nodes/7.CP9fkn1L.js +1 -0
  56. package/admin-build/_app/immutable/nodes/8.BTzDb---.js +1 -0
  57. package/admin-build/_app/immutable/nodes/9.DkNJg_J6.js +1 -0
  58. package/admin-build/_app/version.json +1 -1
  59. package/admin-build/index.html +7 -7
  60. package/package.json +3 -3
  61. package/src/__tests__/database-do-route-validation.test.ts +10 -7
  62. package/src/__tests__/meta-route-registration.test.ts +20 -15
  63. package/src/__tests__/push-handlers.test.ts +1 -1
  64. package/src/__tests__/room-auth-state-loss.test.ts +122 -0
  65. package/src/__tests__/room-handler-context.test.ts +4 -4
  66. package/src/__tests__/room-rate-limit-scopes.test.ts +38 -0
  67. package/src/__tests__/room-runtime-routing.test.ts +23 -0
  68. package/src/__tests__/route-parser.test.ts +6 -0
  69. package/src/__tests__/runtime-startup.test.ts +49 -0
  70. package/src/__tests__/schema.test.ts +15 -6
  71. package/src/durable-objects/database-do.ts +21 -1
  72. package/src/durable-objects/database-live-do.ts +15 -0
  73. package/src/durable-objects/room-runtime-base.ts +436 -169
  74. package/src/durable-objects/rooms-do.ts +63 -25
  75. package/src/index.ts +340 -280
  76. package/src/lib/d1-handler.ts +32 -17
  77. package/src/lib/postgres-handler.ts +24 -12
  78. package/src/lib/route-parser.ts +3 -0
  79. package/src/lib/runtime-startup.ts +53 -0
  80. package/src/lib/schemas.ts +12 -2
  81. package/src/middleware/captcha-verify.ts +16 -3
  82. package/src/middleware/error-handler.ts +1 -1
  83. package/src/middleware/rules.ts +28 -9
  84. package/src/routes/admin-auth.ts +3 -3
  85. package/src/routes/admin.ts +13 -8
  86. package/src/routes/analytics-api.ts +3 -3
  87. package/src/routes/auth.ts +1 -1
  88. package/src/routes/backup.ts +1 -1
  89. package/src/routes/d1.ts +14 -7
  90. package/src/routes/database-live.ts +13 -6
  91. package/src/routes/kv.ts +21 -10
  92. package/src/routes/oauth.ts +1 -1
  93. package/src/routes/push.ts +119 -77
  94. package/src/routes/room.ts +215 -7
  95. package/src/routes/schema-endpoint.ts +2 -2
  96. package/src/routes/sql.ts +10 -6
  97. package/src/routes/storage.ts +4 -2
  98. package/src/routes/vectorize.ts +16 -4
  99. package/admin-build/_app/immutable/chunks/BYI6CUvd.js +0 -1
  100. package/admin-build/_app/immutable/chunks/C6lpZLE2.js +0 -1
  101. package/admin-build/_app/immutable/chunks/CoI6jjbg.js +0 -2
  102. package/admin-build/_app/immutable/chunks/D5GswVnI.js +0 -128
  103. package/admin-build/_app/immutable/chunks/Dj-E9-FO.js +0 -1
  104. package/admin-build/_app/immutable/chunks/g_-Kpxu3.js +0 -1
  105. package/admin-build/_app/immutable/chunks/wCNueVYy.js +0 -1
  106. package/admin-build/_app/immutable/entry/start.CtsqDyfj.js +0 -1
  107. package/admin-build/_app/immutable/nodes/1.B4sI5cB4.js +0 -1
  108. package/admin-build/_app/immutable/nodes/10.D6hvCer6.js +0 -1
  109. package/admin-build/_app/immutable/nodes/11.Dx7b8aQ5.js +0 -1
  110. package/admin-build/_app/immutable/nodes/12.Bqmy5KIF.js +0 -1
  111. package/admin-build/_app/immutable/nodes/13.CC6KpXgS.js +0 -110
  112. package/admin-build/_app/immutable/nodes/14.yCo1Ix8E.js +0 -3
  113. package/admin-build/_app/immutable/nodes/15.co0UfPlh.js +0 -1
  114. package/admin-build/_app/immutable/nodes/18.JUoLOZxh.js +0 -1
  115. package/admin-build/_app/immutable/nodes/19.ND8kmQJe.js +0 -2
  116. package/admin-build/_app/immutable/nodes/21.cz3IN9Cc.js +0 -1
  117. package/admin-build/_app/immutable/nodes/22.UOzm8WYV.js +0 -1
  118. package/admin-build/_app/immutable/nodes/24.DN9usmUs.js +0 -2
  119. package/admin-build/_app/immutable/nodes/25.BddRfAyE.js +0 -2
  120. package/admin-build/_app/immutable/nodes/26.Dl6XHIeT.js +0 -1
  121. package/admin-build/_app/immutable/nodes/27.D0iNwALG.js +0 -1
  122. package/admin-build/_app/immutable/nodes/28.9dKQmdGi.js +0 -1
  123. package/admin-build/_app/immutable/nodes/29.wXzfJUXp.js +0 -1
  124. package/admin-build/_app/immutable/nodes/30.BtZETNsL.js +0 -1
  125. package/admin-build/_app/immutable/nodes/31.CYonj2Jh.js +0 -1
  126. package/admin-build/_app/immutable/nodes/4.COtDPQ9b.js +0 -1
  127. package/admin-build/_app/immutable/nodes/5.CTRCeIhp.js +0 -1
  128. package/admin-build/_app/immutable/nodes/6.ChHi3QkR.js +0 -1
  129. package/admin-build/_app/immutable/nodes/7.CCMtr6Ac.js +0 -1
  130. package/admin-build/_app/immutable/nodes/8.DpWJ-X_-.js +0 -1
  131. package/admin-build/_app/immutable/nodes/9.DOkvfmir.js +0 -1
@@ -0,0 +1,49 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ vi.mock('cloudflare:workers', () => ({
4
+ DurableObject: class DurableObject {},
5
+ }));
6
+
7
+ afterEach(() => {
8
+ vi.resetModules();
9
+ if (typeof globalThis === 'object' && globalThis !== null) {
10
+ delete (globalThis as Record<string, unknown>).__EDGEBASE_RUNTIME_CONFIG__;
11
+ }
12
+ });
13
+
14
+ describe('runtime startup bootstrap', () => {
15
+ it('initializes runtime config idempotently for lazy server and DO entrypoints', async () => {
16
+ const { ensureServerStartup } = await import('../lib/runtime-startup.js');
17
+ const { parseConfig } = await import('../lib/do-router.js');
18
+
19
+ await expect(ensureServerStartup()).resolves.toBeUndefined();
20
+ const firstConfig = parseConfig();
21
+
22
+ await expect(ensureServerStartup()).resolves.toBeUndefined();
23
+ const secondConfig = parseConfig();
24
+
25
+ expect(firstConfig).toEqual(secondConfig);
26
+ expect(secondConfig).toBeTypeOf('object');
27
+ });
28
+
29
+ it('does not clobber an explicitly injected runtime config', async () => {
30
+ const { ensureServerStartup } = await import('../lib/runtime-startup.js');
31
+ const { parseConfig, setConfig } = await import('../lib/do-router.js');
32
+
33
+ setConfig({
34
+ release: false,
35
+ auth: {
36
+ allowedRedirectUrls: ['http://localhost:4173'],
37
+ },
38
+ });
39
+
40
+ await expect(ensureServerStartup()).resolves.toBeUndefined();
41
+
42
+ expect(parseConfig()).toMatchObject({
43
+ release: false,
44
+ auth: {
45
+ allowedRedirectUrls: ['http://localhost:4173'],
46
+ },
47
+ });
48
+ });
49
+ });
@@ -825,10 +825,10 @@ describe('zodDefaultHook', () => {
825
825
  const c = mockContext();
826
826
  const result = zodDefaultHook({
827
827
  success: false,
828
- error: { issues: [{ message: 'field required' }, { message: 'invalid type' }] },
828
+ error: { issues: [{ message: 'field required', path: ['body', 'email'] }, { message: 'invalid type' }] },
829
829
  }, c);
830
830
  expect(result).toBeDefined();
831
- expect(c.lastJson).toEqual({ code: 400, message: 'field required, invalid type' });
831
+ expect(c.lastJson).toEqual({ code: 400, message: 'body.email: field required, invalid type' });
832
832
  expect(c.lastStatus).toBe(400);
833
833
  });
834
834
 
@@ -841,13 +841,13 @@ describe('zodDefaultHook', () => {
841
841
  expect(c.lastJson).toEqual({ code: 400, message: 'too short' });
842
842
  });
843
843
 
844
- it('handles empty issues → empty message', () => {
844
+ it('handles empty issues → default message', () => {
845
845
  const c = mockContext();
846
846
  zodDefaultHook({
847
847
  success: false,
848
848
  error: { issues: [] },
849
849
  }, c);
850
- expect(c.lastJson).toEqual({ code: 400, message: '' });
850
+ expect(c.lastJson).toEqual({ code: 400, message: 'Request validation failed.' });
851
851
  });
852
852
 
853
853
  it('handles missing error.issues and error.errors', () => {
@@ -856,7 +856,7 @@ describe('zodDefaultHook', () => {
856
856
  success: false,
857
857
  error: {},
858
858
  }, c);
859
- expect(c.lastJson).toEqual({ code: 400, message: '' });
859
+ expect(c.lastJson).toEqual({ code: 400, message: 'Request validation failed.' });
860
860
  });
861
861
 
862
862
  it('handles undefined error', () => {
@@ -864,7 +864,16 @@ describe('zodDefaultHook', () => {
864
864
  zodDefaultHook({
865
865
  success: false,
866
866
  }, c);
867
- expect(c.lastJson).toEqual({ code: 400, message: '' });
867
+ expect(c.lastJson).toEqual({ code: 400, message: 'Request validation failed.' });
868
+ });
869
+
870
+ it('formats array indexes in issue paths', () => {
871
+ const c = mockContext();
872
+ zodDefaultHook({
873
+ success: false,
874
+ error: { issues: [{ message: 'Expected string', path: ['body', 'members', 0, 'email'] }] },
875
+ }, c);
876
+ expect(c.lastJson).toEqual({ code: 400, message: 'body.members[0].email: Expected string' });
868
877
  });
869
878
  });
870
879
 
@@ -60,6 +60,7 @@ import { buildDbLiveChannel, DATABASE_LIVE_HUB_DO_NAME } from '../lib/database-l
60
60
  import { resolveRootServiceKey } from '../lib/service-key.js';
61
61
  import { resolveDbLiveBatchThreshold } from '../lib/database-live-config.js';
62
62
  import { buildTableHookRuntimeServices } from '../lib/table-hook-runtime.js';
63
+ import { ensureServerStartup } from '../lib/runtime-startup.js';
63
64
  import type { Env } from '../types.js';
64
65
 
65
66
  // ─── Types ───
@@ -80,6 +81,7 @@ export class DatabaseDO extends DurableObject<DOEnv> {
80
81
  private config: EdgeBaseConfig;
81
82
  private initialized = false;
82
83
  private doName = '';
84
+ private runtimeReadyPromise: Promise<void> | null = null;
83
85
 
84
86
  constructor(ctx: DurableObjectState, env: DOEnv) {
85
87
  super(ctx, env);
@@ -92,6 +94,7 @@ export class DatabaseDO extends DurableObject<DOEnv> {
92
94
  }
93
95
 
94
96
  async fetch(request: Request): Promise<Response> {
97
+ await this.ensureRuntimeReady();
95
98
  // Determine DO name from header or URL
96
99
  const doNameHeader = request.headers.get('X-DO-Name');
97
100
 
@@ -1933,7 +1936,13 @@ export class DatabaseDO extends DurableObject<DOEnv> {
1933
1936
  return c.json(normalizedDbError.toJSON(), normalizedDbError.code as 400);
1934
1937
  }
1935
1938
  console.error('DatabaseDO Error:', err);
1936
- return c.json({ code: 500, message: 'Internal server error.' }, 500);
1939
+ return c.json(
1940
+ {
1941
+ code: 500,
1942
+ message: `Database request failed while handling '${c.req.path}'. Check the worker logs for the original exception.`,
1943
+ },
1944
+ 500,
1945
+ );
1937
1946
  });
1938
1947
 
1939
1948
  return app;
@@ -1991,6 +2000,17 @@ export class DatabaseDO extends DurableObject<DOEnv> {
1991
2000
  return getGlobalConfig(env);
1992
2001
  }
1993
2002
 
2003
+ private async ensureRuntimeReady(): Promise<void> {
2004
+ if (!this.runtimeReadyPromise) {
2005
+ this.runtimeReadyPromise = (async () => {
2006
+ await ensureServerStartup();
2007
+ this.config = this.parseConfig(this.env);
2008
+ })();
2009
+ }
2010
+
2011
+ await this.runtimeReadyPromise;
2012
+ }
2013
+
1994
2014
  // ─── Database Live Event Emission ───
1995
2015
 
1996
2016
  /**
@@ -7,6 +7,7 @@ import { verifyAccessToken } from '../lib/jwt.js';
7
7
  import { parseConfig as getGlobalConfig } from '../lib/do-router.js';
8
8
  import { isDbLiveChannel } from '../lib/database-live-emitter.js';
9
9
  import { resolveDbLiveAuthTimeoutMs } from '../lib/database-live-config.js';
10
+ import { ensureServerStartup } from '../lib/runtime-startup.js';
10
11
 
11
12
  interface DOEnv {
12
13
  JWT_USER_SECRET?: string;
@@ -142,6 +143,7 @@ export class DatabaseLiveDO extends DurableObject<DOEnv> {
142
143
  private pendingAuth = new Map<string, ReturnType<typeof setTimeout>>();
143
144
  private metaCache = new Map<WebSocket, WSMeta>();
144
145
  private recentDeliveryIds = new Map<string, number>();
146
+ private runtimeReadyPromise: Promise<void> | null = null;
145
147
 
146
148
  constructor(ctx: DurableObjectState, env: DOEnv) {
147
149
  super(ctx, env);
@@ -149,6 +151,7 @@ export class DatabaseLiveDO extends DurableObject<DOEnv> {
149
151
  }
150
152
 
151
153
  async fetch(request: Request): Promise<Response> {
154
+ await this.ensureRuntimeReady();
152
155
  const url = new URL(request.url);
153
156
 
154
157
  if (url.pathname === '/internal/event') {
@@ -218,6 +221,7 @@ export class DatabaseLiveDO extends DurableObject<DOEnv> {
218
221
  }
219
222
 
220
223
  async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void> {
224
+ await this.ensureRuntimeReady();
221
225
  if (typeof message !== 'string') return;
222
226
 
223
227
  let msg: Record<string, unknown>;
@@ -922,6 +926,17 @@ export class DatabaseLiveDO extends DurableObject<DOEnv> {
922
926
  if (parts.length >= 5) return parts[3];
923
927
  return null;
924
928
  }
929
+
930
+ private async ensureRuntimeReady(): Promise<void> {
931
+ if (!this.runtimeReadyPromise) {
932
+ this.runtimeReadyPromise = (async () => {
933
+ await ensureServerStartup();
934
+ this.config = getGlobalConfig(this.env);
935
+ })();
936
+ }
937
+
938
+ await this.runtimeReadyPromise;
939
+ }
925
940
  }
926
941
 
927
942
  export function evaluateDatabaseLiveFilters(