@edge-base/server 0.2.0 → 0.2.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.
- package/admin-build/_app/immutable/assets/19.4Si2ZFC_.css +1 -0
- package/admin-build/_app/immutable/assets/{3.Dg81Pgmd.css → 3.BtHYobTg.css} +1 -1
- package/admin-build/_app/immutable/assets/SqlEditor.Bbp1RIk0.css +1 -0
- package/admin-build/_app/immutable/assets/TableSqlTab.yeNZfhgG.css +1 -0
- package/admin-build/_app/immutable/chunks/4vlsb8ej.js +1 -0
- package/admin-build/_app/immutable/chunks/{CfPHB4r5.js → 5PDcRlfX.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BSfSfeDG.js → B8DT4fss.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DP9kmlCd.js → BEYYl662.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B-WlnirM.js → BKXmgPq4.js} +1 -1
- package/admin-build/_app/immutable/chunks/{cqSkc6KP.js → BWyDPAjM.js} +1 -1
- package/admin-build/_app/immutable/chunks/{mD4EETH_.js → BaCHY17I.js} +1 -1
- package/admin-build/_app/immutable/chunks/C-DsDCNG.js +128 -0
- package/admin-build/_app/immutable/chunks/{2nyN5wuZ.js → C85dMlzL.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DgxOZ3uv.js → CPdXvRUb.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DpuSetmN.js → CTngeX8H.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BKLsgaNT.js → DzXaj-Ja.js} +1 -1
- package/admin-build/_app/immutable/chunks/{D43CH5ty.js → c5iKSdWY.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B14gOIqE.js → g3ZZdY-r.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CN6aakgF.js → kiJ6KthZ.js} +1 -1
- package/admin-build/_app/immutable/chunks/lSpxLU5p.js +2 -0
- package/admin-build/_app/immutable/chunks/{uboHVq-x.js → qiZXAKh-.js} +1 -1
- package/admin-build/_app/immutable/entry/{app.Dc071f6C.js → app.BZxfavhF.js} +2 -2
- package/admin-build/_app/immutable/entry/start.Mr9mmopc.js +1 -0
- package/admin-build/_app/immutable/nodes/0.DlsaydXO.js +1 -0
- package/admin-build/_app/immutable/nodes/{1.rMaczUKT.js → 1.D2NWN5eG.js} +1 -1
- package/admin-build/_app/immutable/nodes/{10.DIOlO4hv.js → 10.EMDaN3nw.js} +1 -1
- package/admin-build/_app/immutable/nodes/{11.WxD9E0Eq.js → 11.BasqQ_o9.js} +1 -1
- package/admin-build/_app/immutable/nodes/{12.CNcefK3l.js → 12.DO31Ljs7.js} +1 -1
- package/admin-build/_app/immutable/nodes/{13.aAWsqDdR.js → 13.DhyAy-GZ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{14.C9hdr3EN.js → 14.CLecGWc4.js} +1 -1
- package/admin-build/_app/immutable/nodes/{15.43r5uVx5.js → 15.B9kp3W4e.js} +1 -1
- package/admin-build/_app/immutable/nodes/{16.D519948J.js → 16.Pu_8T3RI.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.ks4I4yoH.js → 17.DX4z43t6.js} +1 -1
- package/admin-build/_app/immutable/nodes/{18.ZuNm22dY.js → 18.BKsSaxrr.js} +1 -1
- package/admin-build/_app/immutable/nodes/19.DXNF1htN.js +2 -0
- package/admin-build/_app/immutable/nodes/{20.C9ASlwCn.js → 20.VRVb0wee.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.Ck3_0D2f.js +1 -0
- package/admin-build/_app/immutable/nodes/{22.6k8cg0Pr.js → 22.DqZf4CtH.js} +1 -1
- package/admin-build/_app/immutable/nodes/{23.B9hcFTU-.js → 23.DtyxMiQG.js} +1 -1
- package/admin-build/_app/immutable/nodes/{24.OsQM9QtS.js → 24.CloWNmTd.js} +1 -1
- package/admin-build/_app/immutable/nodes/{25.ClwkdaPp.js → 25.CnZWMq7_.js} +1 -1
- package/admin-build/_app/immutable/nodes/26.DrV7XOmf.js +1 -0
- package/admin-build/_app/immutable/nodes/{27.J1QASB3b.js → 27.DV8L32OF.js} +1 -1
- package/admin-build/_app/immutable/nodes/{28.BKP1tVcZ.js → 28.Stil2D4u.js} +1 -1
- package/admin-build/_app/immutable/nodes/{29.mqIe62On.js → 29.Zsm1e5Dc.js} +1 -1
- package/admin-build/_app/immutable/nodes/3.CKoj2vNz.js +2 -0
- package/admin-build/_app/immutable/nodes/{30.BRk-4B3j.js → 30.Ni0k5bER.js} +1 -1
- package/admin-build/_app/immutable/nodes/{31.BBqGNVXN.js → 31.mnqj9EbV.js} +1 -1
- package/admin-build/_app/immutable/nodes/{4.Bi91lv2V.js → 4.B_-z9AzT.js} +1 -1
- package/admin-build/_app/immutable/nodes/{5.BumjsbNK.js → 5.yiZ72j4k.js} +1 -1
- package/admin-build/_app/immutable/nodes/{6.CMTP_7xN.js → 6.BqykybBG.js} +1 -1
- package/admin-build/_app/immutable/nodes/{7.4T4wo7Kg.js → 7.BDAHlhsF.js} +1 -1
- package/admin-build/_app/immutable/nodes/{8.MUZQPNsN.js → 8.D8Xvy0lH.js} +1 -1
- package/admin-build/_app/immutable/nodes/{9.3SV00WXe.js → 9.Dddmd7_F.js} +1 -1
- package/admin-build/_app/version.json +1 -1
- package/admin-build/index.html +7 -7
- package/package.json +3 -2
- package/src/__tests__/functions-context.test.ts +5 -5
- package/src/__tests__/meta-export-coverage.test.ts +1 -0
- package/src/__tests__/pagination.test.ts +12 -8
- package/src/__tests__/postgres-dialect.test.ts +2 -2
- package/src/__tests__/query.test.ts +7 -7
- package/src/durable-objects/database-do.ts +3 -3
- package/src/durable-objects/logs-do.ts +2 -2
- package/src/lib/auth-d1-service.ts +1 -1
- package/src/lib/auth-d1.ts +10 -0
- package/src/lib/d1-handler.ts +23 -4
- package/src/lib/functions.ts +204 -397
- package/src/lib/internal-transport.ts +316 -0
- package/src/lib/pagination.ts +3 -3
- package/src/lib/plugin-migrations.ts +2 -2
- package/src/lib/postgres-handler.ts +2 -2
- package/src/lib/query-engine.ts +2 -2
- package/src/middleware/rate-limit.ts +11 -11
- package/src/routes/admin.ts +9 -3
- package/src/routes/auth.ts +13 -12
- package/src/routes/storage.ts +6 -12
- package/src/types.ts +2 -0
- package/admin-build/_app/immutable/assets/TableSqlTab.BHquaMBM.css +0 -1
- package/admin-build/_app/immutable/chunks/CkdaVlhQ.js +0 -2
- package/admin-build/_app/immutable/chunks/D8Nrx_IG.js +0 -128
- package/admin-build/_app/immutable/entry/start.Bhlxoqtt.js +0 -1
- package/admin-build/_app/immutable/nodes/0.CCfcYVV2.js +0 -1
- package/admin-build/_app/immutable/nodes/19.D519948J.js +0 -1
- package/admin-build/_app/immutable/nodes/21.BhSD2EfX.js +0 -1
- package/admin-build/_app/immutable/nodes/26._-65WG0q.js +0 -1
- package/admin-build/_app/immutable/nodes/3.WkDZWDQC.js +0 -2
package/admin-build/index.html
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>EdgeBase Admin</title>
|
|
7
7
|
<link rel="icon" href="/admin/favicon.svg" type="image/svg+xml" />
|
|
8
|
-
<link href="/admin/_app/immutable/entry/start.
|
|
9
|
-
<link href="/admin/_app/immutable/chunks/
|
|
8
|
+
<link href="/admin/_app/immutable/entry/start.Mr9mmopc.js" rel="modulepreload">
|
|
9
|
+
<link href="/admin/_app/immutable/chunks/qiZXAKh-.js" rel="modulepreload">
|
|
10
10
|
<link href="/admin/_app/immutable/chunks/BdTBlfLy.js" rel="modulepreload">
|
|
11
11
|
<link href="/admin/_app/immutable/chunks/Bn2NtlTj.js" rel="modulepreload">
|
|
12
|
-
<link href="/admin/_app/immutable/chunks/
|
|
13
|
-
<link href="/admin/_app/immutable/entry/app.
|
|
12
|
+
<link href="/admin/_app/immutable/chunks/5PDcRlfX.js" rel="modulepreload">
|
|
13
|
+
<link href="/admin/_app/immutable/entry/app.BZxfavhF.js" rel="modulepreload">
|
|
14
14
|
<link href="/admin/_app/immutable/chunks/B2bEC_Hm.js" rel="modulepreload">
|
|
15
15
|
<link href="/admin/_app/immutable/chunks/Bb0e0sAP.js" rel="modulepreload">
|
|
16
16
|
<link href="/admin/_app/immutable/chunks/DtZk82gG.js" rel="modulepreload">
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<div style="display: contents">
|
|
27
27
|
<script>
|
|
28
28
|
{
|
|
29
|
-
|
|
29
|
+
__sveltekit_1ddhohc = {
|
|
30
30
|
base: "/admin",
|
|
31
31
|
assets: "/admin"
|
|
32
32
|
};
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
const element = document.currentScript.parentElement;
|
|
35
35
|
|
|
36
36
|
Promise.all([
|
|
37
|
-
import("/admin/_app/immutable/entry/start.
|
|
38
|
-
import("/admin/_app/immutable/entry/app.
|
|
37
|
+
import("/admin/_app/immutable/entry/start.Mr9mmopc.js"),
|
|
38
|
+
import("/admin/_app/immutable/entry/app.BZxfavhF.js")
|
|
39
39
|
]).then(([kit, app]) => {
|
|
40
40
|
kit.start(app, element);
|
|
41
41
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edge-base/server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "EdgeBase runtime assets consumed by the EdgeBase CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"jose": "^6.0.0",
|
|
35
35
|
"pg": "^8.16.3",
|
|
36
36
|
"zod": "^4.3.6",
|
|
37
|
-
"@edge-base/shared": "0.2.
|
|
37
|
+
"@edge-base/shared": "0.2.2",
|
|
38
|
+
"@edge-base/core": "0.2.2"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@cloudflare/vitest-pool-workers": "^0.8.71",
|
|
@@ -33,7 +33,7 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
33
33
|
workerUrl: 'http://localhost:8787',
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
const result = await ctx.admin.db('shared').table('posts').
|
|
36
|
+
const result = await ctx.admin.db('shared').table('posts').limit(5).getList();
|
|
37
37
|
|
|
38
38
|
expect(result.items).toHaveLength(1);
|
|
39
39
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
@@ -97,7 +97,7 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
97
97
|
);
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
it('normalizes admin.
|
|
100
|
+
it('normalizes admin.sqlWithDirectD1Access worker responses to row arrays', async () => {
|
|
101
101
|
const fetchMock = vi.fn().mockResolvedValue(
|
|
102
102
|
new Response(JSON.stringify({
|
|
103
103
|
rows: [{ total: 2 }],
|
|
@@ -129,7 +129,7 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
129
129
|
serviceKey: 'sk-test',
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
const rows = await ctx.admin.
|
|
132
|
+
const rows = await ctx.admin.sqlWithDirectD1Access('shared', undefined, 'SELECT COUNT(*) AS total FROM posts');
|
|
133
133
|
|
|
134
134
|
expect(rows).toEqual([{ total: 2 }]);
|
|
135
135
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
@@ -144,7 +144,7 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
144
144
|
);
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
-
it('routes admin.
|
|
147
|
+
it('routes admin.sqlWithDirectD1Access through the database DO when env is available', async () => {
|
|
148
148
|
const fetchMock = vi.fn();
|
|
149
149
|
vi.stubGlobal('fetch', fetchMock);
|
|
150
150
|
|
|
@@ -196,7 +196,7 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
196
196
|
serviceKey: 'sk-test',
|
|
197
197
|
});
|
|
198
198
|
|
|
199
|
-
const rows = await ctx.admin.
|
|
199
|
+
const rows = await ctx.admin.sqlWithDirectD1Access('workspace', 'ws-1', 'SELECT COUNT(*) AS total FROM members', []);
|
|
200
200
|
|
|
201
201
|
expect(rows).toEqual([{ total: 3 }]);
|
|
202
202
|
expect(stub.fetch).toHaveBeenCalledTimes(2);
|
|
@@ -28,6 +28,7 @@ const UNTESTED_LIBS = new Set([
|
|
|
28
28
|
'email-provider.ts',
|
|
29
29
|
'email-translations.ts', // email i18n strings — used by auth email flows, tested via integration
|
|
30
30
|
'functions.ts',
|
|
31
|
+
'internal-transport.ts', // internal transport adapter — tested indirectly via functions-context tests
|
|
31
32
|
'hono.ts',
|
|
32
33
|
'log-writer.ts',
|
|
33
34
|
'plugin-migrations.ts',
|
|
@@ -10,7 +10,7 @@ import { parsePagination } from '../lib/pagination.js';
|
|
|
10
10
|
describe('parsePagination', () => {
|
|
11
11
|
// ── Defaults ──
|
|
12
12
|
it('returns defaults when no params provided', () => {
|
|
13
|
-
expect(parsePagination(undefined, undefined)).toEqual({ limit:
|
|
13
|
+
expect(parsePagination(undefined, undefined)).toEqual({ limit: 100, offset: 0 });
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
// ── Valid values ──
|
|
@@ -23,21 +23,25 @@ describe('parsePagination', () => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
// ── Limit clamping ──
|
|
26
|
-
it('clamps limit to
|
|
27
|
-
expect(parsePagination('
|
|
26
|
+
it('clamps limit to 1000', () => {
|
|
27
|
+
expect(parsePagination('9999', '0')).toEqual({ limit: 1000, offset: 0 });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('accepts limit within range (500)', () => {
|
|
31
|
+
expect(parsePagination('500', '0')).toEqual({ limit: 500, offset: 0 });
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
it('rejects limit=0 (falls back to default)', () => {
|
|
31
|
-
expect(parsePagination('0', '0')).toEqual({ limit:
|
|
35
|
+
expect(parsePagination('0', '0')).toEqual({ limit: 100, offset: 0 });
|
|
32
36
|
});
|
|
33
37
|
|
|
34
38
|
// ── REGRESSION: negative and NaN values ──
|
|
35
39
|
it('rejects negative limit', () => {
|
|
36
|
-
expect(parsePagination('-5', '0')).toEqual({ limit:
|
|
40
|
+
expect(parsePagination('-5', '0')).toEqual({ limit: 100, offset: 0 });
|
|
37
41
|
});
|
|
38
42
|
|
|
39
43
|
it('rejects NaN limit', () => {
|
|
40
|
-
expect(parsePagination('abc', '0')).toEqual({ limit:
|
|
44
|
+
expect(parsePagination('abc', '0')).toEqual({ limit: 100, offset: 0 });
|
|
41
45
|
});
|
|
42
46
|
|
|
43
47
|
it('rejects negative offset', () => {
|
|
@@ -49,11 +53,11 @@ describe('parsePagination', () => {
|
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
it('rejects Infinity limit', () => {
|
|
52
|
-
expect(parsePagination('Infinity', '0')).toEqual({ limit:
|
|
56
|
+
expect(parsePagination('Infinity', '0')).toEqual({ limit: 100, offset: 0 });
|
|
53
57
|
});
|
|
54
58
|
|
|
55
59
|
// ── Empty string (same as undefined) ──
|
|
56
60
|
it('treats empty strings as defaults', () => {
|
|
57
|
-
expect(parsePagination('', '')).toEqual({ limit:
|
|
61
|
+
expect(parsePagination('', '')).toEqual({ limit: 100, offset: 0 });
|
|
58
62
|
});
|
|
59
63
|
});
|
|
@@ -43,7 +43,7 @@ describe('PostgreSQL dialect — bind params', () => {
|
|
|
43
43
|
const { sql, params } = buildListQuery('products', {}, 'postgres');
|
|
44
44
|
expect(sql).toContain('LIMIT $1');
|
|
45
45
|
expect(sql).not.toContain('?');
|
|
46
|
-
expect(params).toEqual([
|
|
46
|
+
expect(params).toEqual([100]);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it('buildListQuery with offset → $1 OFFSET $2', () => {
|
|
@@ -71,7 +71,7 @@ describe('PostgreSQL dialect — bind params', () => {
|
|
|
71
71
|
expect(sql).toContain('"status" = $1');
|
|
72
72
|
expect(sql).toContain('"price" > $2');
|
|
73
73
|
expect(sql).toContain('LIMIT $3');
|
|
74
|
-
expect(params).toEqual(['active', 100,
|
|
74
|
+
expect(params).toEqual(['active', 100, 100]);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
it('buildListQuery cursor after → $1 for cursor, $2 for limit', () => {
|
|
@@ -79,10 +79,10 @@ describe('buildListQuery — no filters', () => {
|
|
|
79
79
|
expect(sql).toContain('LIMIT');
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
it('default limit is
|
|
82
|
+
it('default limit is 100', () => {
|
|
83
83
|
const { params } = buildListQuery('posts', {});
|
|
84
|
-
// params should contain
|
|
85
|
-
expect(params).toContain(
|
|
84
|
+
// params should contain 100 as default limit
|
|
85
|
+
expect(params).toContain(100);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
88
|
it('generates countSql for non-cursor pagination', () => {
|
|
@@ -313,9 +313,9 @@ describe('buildSearchQuery', () => {
|
|
|
313
313
|
expect(sql).toContain('"posts_fts"');
|
|
314
314
|
});
|
|
315
315
|
|
|
316
|
-
it('default limit
|
|
316
|
+
it('default limit 100, offset 0', () => {
|
|
317
317
|
const { params } = buildSearchQuery('posts', 'q');
|
|
318
|
-
expect(params[1]).toBe(
|
|
318
|
+
expect(params[1]).toBe(100);
|
|
319
319
|
expect(params[2]).toBeUndefined();
|
|
320
320
|
});
|
|
321
321
|
|
|
@@ -560,7 +560,7 @@ describe('3-way sync: QUERY_PARAM_KEYS ↔ Zod queryParamsSchema', () => {
|
|
|
560
560
|
describe('buildListQuery — exact params verification', () => {
|
|
561
561
|
it('no filters → params contains only default limit', () => {
|
|
562
562
|
const { params } = buildListQuery('t', {});
|
|
563
|
-
expect(params).toEqual([
|
|
563
|
+
expect(params).toEqual([100]);
|
|
564
564
|
});
|
|
565
565
|
|
|
566
566
|
it('no filters → countParams is empty array', () => {
|
|
@@ -722,7 +722,7 @@ describe('buildSubstringSearchQuery', () => {
|
|
|
722
722
|
const { sql, params } = buildSubstringSearchQuery('posts', '준규', { fields: ['title', 'content'] });
|
|
723
723
|
expect(sql).toContain('instr(lower(CAST("title" AS TEXT)), lower(?)) > 0');
|
|
724
724
|
expect(sql).toContain('instr(lower(CAST("content" AS TEXT)), lower(?)) > 0');
|
|
725
|
-
expect(params).toEqual(['준규', '준규',
|
|
725
|
+
expect(params).toEqual(['준규', '준규', 100]);
|
|
726
726
|
});
|
|
727
727
|
|
|
728
728
|
it('passes the raw term through the SQLite instr() fallback', () => {
|
|
@@ -470,7 +470,7 @@ export class DatabaseDO extends DurableObject<DOEnv> {
|
|
|
470
470
|
if (countSql && countParams) {
|
|
471
471
|
const countResult = [...this.sql(countSql, ...countParams)];
|
|
472
472
|
const total = (countResult[0]?.total as number) ?? 0;
|
|
473
|
-
const perPage = options.pagination?.perPage ?? options.pagination?.limit ??
|
|
473
|
+
const perPage = options.pagination?.perPage ?? options.pagination?.limit ?? 100;
|
|
474
474
|
response.total = total;
|
|
475
475
|
response.page = options.pagination?.page ?? 1;
|
|
476
476
|
response.perPage = perPage;
|
|
@@ -478,7 +478,7 @@ export class DatabaseDO extends DurableObject<DOEnv> {
|
|
|
478
478
|
|
|
479
479
|
// Cursor pagination: always include cursor and hasMore when items exist
|
|
480
480
|
// so clients can start cursor-based pagination from any page (including the first)
|
|
481
|
-
const limit = options.pagination?.limit ?? options.pagination?.perPage ??
|
|
481
|
+
const limit = options.pagination?.limit ?? options.pagination?.perPage ?? 100;
|
|
482
482
|
const hasMore = normalizedRows.length === limit;
|
|
483
483
|
response.hasMore = hasMore;
|
|
484
484
|
if (normalizedRows.length > 0) {
|
|
@@ -516,7 +516,7 @@ export class DatabaseDO extends DurableObject<DOEnv> {
|
|
|
516
516
|
return c.json({ items: [] });
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
-
const limit = options.pagination?.limit ?? options.pagination?.perPage ??
|
|
519
|
+
const limit = options.pagination?.limit ?? options.pagination?.perPage ?? 100;
|
|
520
520
|
const offset = options.pagination?.offset ?? ((options.pagination?.page ?? 1) - 1) * limit;
|
|
521
521
|
|
|
522
522
|
const tableConfig = this.getTableConfig(name);
|
|
@@ -484,10 +484,10 @@ export class LogsDO extends DurableObject<LogsDOEnv> {
|
|
|
484
484
|
whereParts.push('status >= ?');
|
|
485
485
|
params.push(SERVER_ERROR_STATUS);
|
|
486
486
|
} else if (level === 'warn') {
|
|
487
|
-
whereParts.push('status >= ? AND status < ?');
|
|
487
|
+
whereParts.push('(status >= ? AND status < ? AND status != 304)');
|
|
488
488
|
params.push(300, SERVER_ERROR_STATUS);
|
|
489
489
|
} else if (level === 'info') {
|
|
490
|
-
whereParts.push('status >= ? AND status < ?');
|
|
490
|
+
whereParts.push('(status >= ? AND status < ? OR status = 304)');
|
|
491
491
|
params.push(200, 300);
|
|
492
492
|
}
|
|
493
493
|
|
|
@@ -194,7 +194,7 @@ export async function updateUser(
|
|
|
194
194
|
'email', 'passwordHash', 'displayName', 'avatarUrl', 'emailVisibility',
|
|
195
195
|
'role', 'status', 'verified', 'isAnonymous', 'locale', 'metadata', 'appMetadata',
|
|
196
196
|
'customClaims', 'phone', 'phoneVerified', 'disabled', 'bannedUntil',
|
|
197
|
-
'lastSignInAt', 'updatedAt',
|
|
197
|
+
'lastSignInAt', 'lastSignedInAt', 'updatedAt',
|
|
198
198
|
]);
|
|
199
199
|
|
|
200
200
|
const sets: string[] = [];
|
package/src/lib/auth-d1.ts
CHANGED
|
@@ -125,6 +125,7 @@ CREATE TABLE IF NOT EXISTS _users (
|
|
|
125
125
|
disabled INTEGER DEFAULT 0,
|
|
126
126
|
status TEXT DEFAULT 'active',
|
|
127
127
|
locale TEXT DEFAULT 'en',
|
|
128
|
+
lastSignedInAt TEXT,
|
|
128
129
|
createdAt TEXT NOT NULL,
|
|
129
130
|
updatedAt TEXT NOT NULL
|
|
130
131
|
);
|
|
@@ -309,6 +310,7 @@ CREATE TABLE IF NOT EXISTS _users (
|
|
|
309
310
|
disabled INTEGER DEFAULT 0,
|
|
310
311
|
status TEXT DEFAULT 'active',
|
|
311
312
|
locale TEXT DEFAULT 'en',
|
|
313
|
+
lastSignedInAt TEXT,
|
|
312
314
|
createdAt TEXT NOT NULL,
|
|
313
315
|
updatedAt TEXT NOT NULL
|
|
314
316
|
);
|
|
@@ -403,6 +405,14 @@ export async function ensureAuthSchema(db: AuthDb): Promise<void> {
|
|
|
403
405
|
.filter((s) => s.length > 0);
|
|
404
406
|
|
|
405
407
|
await db.batch(statements.map((sql) => ({ sql })));
|
|
408
|
+
|
|
409
|
+
// Migrate existing _users tables: add lastSignedInAt if missing
|
|
410
|
+
try {
|
|
411
|
+
await db.run('ALTER TABLE _users ADD COLUMN lastSignedInAt TEXT', []);
|
|
412
|
+
} catch {
|
|
413
|
+
// Column already exists — safe to ignore
|
|
414
|
+
}
|
|
415
|
+
|
|
406
416
|
schemaInitialized = true;
|
|
407
417
|
}
|
|
408
418
|
|
package/src/lib/d1-handler.ts
CHANGED
|
@@ -399,8 +399,27 @@ async function handleList(
|
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
const queryOpts = parseQueryParams(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
402
|
-
|
|
403
|
-
|
|
402
|
+
let query = buildListQuery(tableName, queryOpts, 'sqlite');
|
|
403
|
+
let result;
|
|
404
|
+
try {
|
|
405
|
+
result = await executeD1Query(resolved.db, query.sql, query.params);
|
|
406
|
+
} catch {
|
|
407
|
+
// FTS table may not exist — fall back to substring search
|
|
408
|
+
if (queryOpts.search) {
|
|
409
|
+
const searchFields = tableConfig.schema ? Object.keys(tableConfig.schema).filter(k => tableConfig.schema![k] !== false) : ['id'];
|
|
410
|
+
query = buildSubstringSearchQuery(tableName, queryOpts.search, {
|
|
411
|
+
pagination: queryOpts.pagination,
|
|
412
|
+
filters: queryOpts.filters,
|
|
413
|
+
orFilters: queryOpts.orFilters,
|
|
414
|
+
sort: queryOpts.sort,
|
|
415
|
+
fields: searchFields,
|
|
416
|
+
}, 'sqlite');
|
|
417
|
+
result = await executeD1Query(resolved.db, query.sql, query.params);
|
|
418
|
+
} else {
|
|
419
|
+
throw new Error('Query failed');
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const { countSql, countParams } = query;
|
|
404
423
|
|
|
405
424
|
// Apply read rules per row + normalize booleans/JSON
|
|
406
425
|
let items = result.rows.map(r => normalizeRow(stripInternalFields(r), tableConfig));
|
|
@@ -435,7 +454,7 @@ async function handleList(
|
|
|
435
454
|
total = Number(countResult.rows[0]?.total ?? 0);
|
|
436
455
|
}
|
|
437
456
|
|
|
438
|
-
const perPage = queryOpts.pagination?.limit ?? queryOpts.pagination?.perPage ??
|
|
457
|
+
const perPage = queryOpts.pagination?.limit ?? queryOpts.pagination?.perPage ?? 100;
|
|
439
458
|
const page = queryOpts.pagination?.page ?? 1;
|
|
440
459
|
// Always include cursor/hasMore like DO does — clients can start cursor pagination from any page
|
|
441
460
|
const hasMore = items.length === perPage;
|
|
@@ -497,7 +516,7 @@ async function handleSearch(
|
|
|
497
516
|
|
|
498
517
|
let items: Record<string, unknown>[];
|
|
499
518
|
let total = 0;
|
|
500
|
-
const limit = queryOpts.pagination?.limit ?? queryOpts.pagination?.perPage ??
|
|
519
|
+
const limit = queryOpts.pagination?.limit ?? queryOpts.pagination?.perPage ?? 100;
|
|
501
520
|
const offset = queryOpts.pagination?.offset ?? ((queryOpts.pagination?.page ?? 1) - 1) * limit;
|
|
502
521
|
const searchQuery = buildSearchQuery(tableName, searchTerm, {
|
|
503
522
|
pagination: queryOpts.pagination,
|