@nkzw/codiff-service 1.10.0 → 1.11.1
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/api.test.ts +37 -1
- package/api.ts +63 -9
- package/dist/api.mjs +53 -13
- package/dist/fate.mjs +4 -4
- package/dist/index.mjs +2 -2
- package/dist/schema.mjs +1 -0
- package/package.json +3 -3
- package/schema.ts +1 -1
package/api.test.ts
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
import { expect, test } from 'vite-plus/test';
|
|
2
|
-
import { handleSharingApiRequest, type SharingEnv } from './api.ts';
|
|
2
|
+
import { handleSharingApiRequest, readRequestText, type SharingEnv } from './api.ts';
|
|
3
|
+
|
|
4
|
+
test('reads request bodies within a byte limit', async () => {
|
|
5
|
+
const request = new Request('https://codiff.dev/api/uploads', {
|
|
6
|
+
body: new Uint8Array([0x61, 0xe2, 0x82, 0xac]),
|
|
7
|
+
method: 'POST',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
expect(await readRequestText(request, 4)).toBe('a€');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('rejects request bodies that exceed declared or streamed byte limits', async () => {
|
|
14
|
+
const declared = new Request('https://codiff.dev/api/uploads', {
|
|
15
|
+
body: 'small',
|
|
16
|
+
headers: { 'content-length': '100' },
|
|
17
|
+
method: 'POST',
|
|
18
|
+
});
|
|
19
|
+
expect(await readRequestText(declared, 5)).toBeNull();
|
|
20
|
+
|
|
21
|
+
let cancelled = false;
|
|
22
|
+
const streamed = new Request('https://codiff.dev/api/uploads', {
|
|
23
|
+
body: new ReadableStream({
|
|
24
|
+
cancel() {
|
|
25
|
+
cancelled = true;
|
|
26
|
+
throw new Error('Cancellation failed.');
|
|
27
|
+
},
|
|
28
|
+
start(controller) {
|
|
29
|
+
controller.enqueue(new Uint8Array([1, 2, 3]));
|
|
30
|
+
controller.enqueue(new Uint8Array([4, 5, 6]));
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
duplex: 'half',
|
|
34
|
+
method: 'POST',
|
|
35
|
+
} as RequestInit);
|
|
36
|
+
expect(await readRequestText(streamed, 5)).toBeNull();
|
|
37
|
+
expect(cancelled).toBe(true);
|
|
38
|
+
});
|
|
3
39
|
|
|
4
40
|
test('creates anonymous upload intents without touching persistent storage', async () => {
|
|
5
41
|
let databaseAccesses = 0;
|
package/api.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parsePlanShareUpload } from '@nkzw/codiff-core/share';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { db, eq, sql, withDatabase, type D1Binding } from './db.ts';
|
|
3
|
+
import { and, db, eq, sql, withDatabase, type D1Binding } from './db.ts';
|
|
4
4
|
import { plan, shareDailyUsage, uploadIntent, user, walkthrough, type UserRow } from './schema.ts';
|
|
5
5
|
import {
|
|
6
6
|
createUploadIntentSecret,
|
|
@@ -185,6 +185,44 @@ const unauthorized = () => json({ error: 'authentication-required' }, { status:
|
|
|
185
185
|
const maxUploadByteSize = 25 * 1024 * 1024;
|
|
186
186
|
const textEncoder = new TextEncoder();
|
|
187
187
|
|
|
188
|
+
export const readRequestText = async (request: Request, maximumByteSize: number) => {
|
|
189
|
+
const contentLength = request.headers.get('content-length');
|
|
190
|
+
if (contentLength && Number(contentLength) > maximumByteSize) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!request.body) {
|
|
195
|
+
return '';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const reader = request.body.getReader();
|
|
199
|
+
const decoder = new TextDecoder();
|
|
200
|
+
let byteSize = 0;
|
|
201
|
+
const chunks: Array<string> = [];
|
|
202
|
+
try {
|
|
203
|
+
while (true) {
|
|
204
|
+
const { done, value } = await reader.read();
|
|
205
|
+
if (done) {
|
|
206
|
+
chunks.push(decoder.decode());
|
|
207
|
+
return chunks.join('');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
byteSize += value.byteLength;
|
|
211
|
+
if (byteSize > maximumByteSize) {
|
|
212
|
+
try {
|
|
213
|
+
await reader.cancel();
|
|
214
|
+
} catch {
|
|
215
|
+
// Cancellation is best-effort; the size limit still determines the response.
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
chunks.push(decoder.decode(value, { stream: true }));
|
|
220
|
+
}
|
|
221
|
+
} finally {
|
|
222
|
+
reader.releaseLock();
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
188
226
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
189
227
|
value !== null && typeof value === 'object';
|
|
190
228
|
|
|
@@ -517,8 +555,8 @@ const uploadShare = async (request: Request, env: SharingEnv, options: SharingAp
|
|
|
517
555
|
return unauthorized();
|
|
518
556
|
}
|
|
519
557
|
|
|
520
|
-
const body = await request
|
|
521
|
-
if (
|
|
558
|
+
const body = await readRequestText(request, maxUploadByteSize);
|
|
559
|
+
if (body === null) {
|
|
522
560
|
return json({ error: 'manifest-too-large' }, { status: 413 });
|
|
523
561
|
}
|
|
524
562
|
|
|
@@ -547,11 +585,20 @@ const uploadShare = async (request: Request, env: SharingEnv, options: SharingAp
|
|
|
547
585
|
}
|
|
548
586
|
const now = new Date();
|
|
549
587
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
588
|
+
const [ownedIntent] = await db
|
|
589
|
+
.update(uploadIntent)
|
|
590
|
+
.set({ status: 'uploading', updatedAt: now })
|
|
591
|
+
.where(and(eq(uploadIntent.id, intent.id), eq(uploadIntent.status, 'claimed')))
|
|
592
|
+
.returning({ id: uploadIntent.id });
|
|
593
|
+
if (!ownedIntent) {
|
|
594
|
+
return unauthorized();
|
|
595
|
+
}
|
|
553
596
|
|
|
554
597
|
try {
|
|
598
|
+
await env.WALKTHROUGH_BUCKET.put(objectKey, canonical, {
|
|
599
|
+
httpMetadata: { contentType: 'application/json; charset=utf-8' },
|
|
600
|
+
});
|
|
601
|
+
|
|
555
602
|
if (upload.kind === 'walkthrough') {
|
|
556
603
|
const source = upload.snapshot.repository.source;
|
|
557
604
|
const repository = getRepositoryMetadata(source);
|
|
@@ -591,7 +638,7 @@ const uploadShare = async (request: Request, env: SharingEnv, options: SharingAp
|
|
|
591
638
|
updatedAt: now,
|
|
592
639
|
walkthroughSlug: slug,
|
|
593
640
|
})
|
|
594
|
-
.where(eq(uploadIntent.id, intent.id)),
|
|
641
|
+
.where(and(eq(uploadIntent.id, intent.id), eq(uploadIntent.status, 'uploading'))),
|
|
595
642
|
]);
|
|
596
643
|
} else {
|
|
597
644
|
await db.batch([
|
|
@@ -619,11 +666,18 @@ const uploadShare = async (request: Request, env: SharingEnv, options: SharingAp
|
|
|
619
666
|
updatedAt: now,
|
|
620
667
|
walkthroughSlug: slug,
|
|
621
668
|
})
|
|
622
|
-
.where(eq(uploadIntent.id, intent.id)),
|
|
669
|
+
.where(and(eq(uploadIntent.id, intent.id), eq(uploadIntent.status, 'uploading'))),
|
|
623
670
|
]);
|
|
624
671
|
}
|
|
625
672
|
} catch (error) {
|
|
626
|
-
|
|
673
|
+
try {
|
|
674
|
+
await env.WALKTHROUGH_BUCKET.delete(objectKey);
|
|
675
|
+
} finally {
|
|
676
|
+
await db
|
|
677
|
+
.update(uploadIntent)
|
|
678
|
+
.set({ status: 'claimed', updatedAt: new Date() })
|
|
679
|
+
.where(and(eq(uploadIntent.id, intent.id), eq(uploadIntent.status, 'uploading')));
|
|
680
|
+
}
|
|
627
681
|
if (isQuotaError(error)) {
|
|
628
682
|
return quotaResponse();
|
|
629
683
|
}
|
package/dist/api.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { plan, shareDailyUsage, uploadIntent, user, walkthrough } from "./schema.mjs";
|
|
2
|
-
import { db, eq, sql, withDatabase } from "./db.mjs";
|
|
2
|
+
import { and, db, eq, sql, withDatabase } from "./db.mjs";
|
|
3
3
|
import { createUploadIntentSecret, hashUploadIntentSecret, verifyUploadIntentSecret } from "./upload-intent.mjs";
|
|
4
4
|
import { parsePlanShareUpload } from "@nkzw/codiff-core/share";
|
|
5
5
|
import { z } from "zod";
|
|
@@ -14,7 +14,7 @@ const reviewAuthorSchema = z.object({
|
|
|
14
14
|
const reviewCommentSchema = z.object({
|
|
15
15
|
anchor: z.enum(["file", "line"]).optional(),
|
|
16
16
|
author: reviewAuthorSchema,
|
|
17
|
-
body: boundedString(
|
|
17
|
+
body: boundedString(131072),
|
|
18
18
|
filePath: nonEmptyString(4096),
|
|
19
19
|
id: nonEmptyString(256),
|
|
20
20
|
isThreadResolved: z.boolean().optional(),
|
|
@@ -28,7 +28,7 @@ const reviewCommentSchema = z.object({
|
|
|
28
28
|
}).passthrough().refine((comment) => comment.anchor === "file" || comment.lineNumber != null && comment.side != null && (comment.startLineNumber == null || comment.startSide != null), "Invalid walkthrough review comment.");
|
|
29
29
|
const generalCommentSchema = z.object({
|
|
30
30
|
author: reviewAuthorSchema,
|
|
31
|
-
body: boundedString(
|
|
31
|
+
body: boundedString(131072),
|
|
32
32
|
id: nonEmptyString(256),
|
|
33
33
|
submittedAt: boundedString(64).optional()
|
|
34
34
|
}).passthrough();
|
|
@@ -60,7 +60,7 @@ const walkthroughManifestSchema = z.object({
|
|
|
60
60
|
reviewComments: z.array(reviewCommentSchema).max(1e4).optional(),
|
|
61
61
|
version: z.literal(1),
|
|
62
62
|
walkthrough: z.object({
|
|
63
|
-
summary: boundedString(
|
|
63
|
+
summary: boundedString(131072).optional(),
|
|
64
64
|
title: nonEmptyString(300)
|
|
65
65
|
}).passthrough()
|
|
66
66
|
}).passthrough();
|
|
@@ -82,8 +82,36 @@ const json = (value, init) => new Response(JSON.stringify(value), {
|
|
|
82
82
|
const notFound = () => json({ error: "not-found" }, { status: 404 });
|
|
83
83
|
const badRequest = (message) => json({ error: message }, { status: 400 });
|
|
84
84
|
const unauthorized = () => json({ error: "authentication-required" }, { status: 401 });
|
|
85
|
-
const maxUploadByteSize =
|
|
85
|
+
const maxUploadByteSize = 26214400;
|
|
86
86
|
const textEncoder = new TextEncoder();
|
|
87
|
+
const readRequestText = async (request, maximumByteSize) => {
|
|
88
|
+
const contentLength = request.headers.get("content-length");
|
|
89
|
+
if (contentLength && Number(contentLength) > maximumByteSize) return null;
|
|
90
|
+
if (!request.body) return "";
|
|
91
|
+
const reader = request.body.getReader();
|
|
92
|
+
const decoder = new TextDecoder();
|
|
93
|
+
let byteSize = 0;
|
|
94
|
+
const chunks = [];
|
|
95
|
+
try {
|
|
96
|
+
while (true) {
|
|
97
|
+
const { done, value } = await reader.read();
|
|
98
|
+
if (done) {
|
|
99
|
+
chunks.push(decoder.decode());
|
|
100
|
+
return chunks.join("");
|
|
101
|
+
}
|
|
102
|
+
byteSize += value.byteLength;
|
|
103
|
+
if (byteSize > maximumByteSize) {
|
|
104
|
+
try {
|
|
105
|
+
await reader.cancel();
|
|
106
|
+
} catch {}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
chunks.push(decoder.decode(value, { stream: true }));
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
reader.releaseLock();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
87
115
|
const isRecord = (value) => value !== null && typeof value === "object";
|
|
88
116
|
const isBetterAuthApi = (value) => isRecord(value) && isRecord(value.api) && typeof value.api.getSession === "function";
|
|
89
117
|
const stringValue = (value) => typeof value === "string" && value ? value : null;
|
|
@@ -233,7 +261,7 @@ const hasAvailableQuota = async (userId, kind) => {
|
|
|
233
261
|
const createUploadIntent = async (request, env, options) => {
|
|
234
262
|
const code = randomCode();
|
|
235
263
|
const now = /* @__PURE__ */ new Date();
|
|
236
|
-
const expiresAt = new Date(now.getTime() +
|
|
264
|
+
const expiresAt = new Date(now.getTime() + 6e5);
|
|
237
265
|
const secret = await createUploadIntentSecret(env, code, expiresAt);
|
|
238
266
|
const tokenHash = await hashUploadIntentSecret(secret);
|
|
239
267
|
const shareUser = await getAuthenticatedShareUser(request, options.auth);
|
|
@@ -281,8 +309,8 @@ const uploadShare = async (request, env, options) => {
|
|
|
281
309
|
if (!code || !token) return unauthorized();
|
|
282
310
|
const [intent] = await db.select().from(uploadIntent).where(eq(uploadIntent.code, code)).limit(1);
|
|
283
311
|
if (!intent || intent.status !== "claimed" || !intent.sharedByUserId || intent.expiresAt.getTime() < Date.now() || intent.uploadTokenHash !== await sha256(token)) return unauthorized();
|
|
284
|
-
const body = await request
|
|
285
|
-
if (
|
|
312
|
+
const body = await readRequestText(request, maxUploadByteSize);
|
|
313
|
+
if (body === null) return json({ error: "manifest-too-large" }, { status: 413 });
|
|
286
314
|
let upload;
|
|
287
315
|
try {
|
|
288
316
|
upload = parseShareUpload(body);
|
|
@@ -297,8 +325,13 @@ const uploadShare = async (request, env, options) => {
|
|
|
297
325
|
const shareUser = await getShareUserById(intent.sharedByUserId);
|
|
298
326
|
if (!shareUser) return unauthorized();
|
|
299
327
|
const now = /* @__PURE__ */ new Date();
|
|
300
|
-
await
|
|
328
|
+
const [ownedIntent] = await db.update(uploadIntent).set({
|
|
329
|
+
status: "uploading",
|
|
330
|
+
updatedAt: now
|
|
331
|
+
}).where(and(eq(uploadIntent.id, intent.id), eq(uploadIntent.status, "claimed"))).returning({ id: uploadIntent.id });
|
|
332
|
+
if (!ownedIntent) return unauthorized();
|
|
301
333
|
try {
|
|
334
|
+
await env.WALKTHROUGH_BUCKET.put(objectKey, canonical, { httpMetadata: { contentType: "application/json; charset=utf-8" } });
|
|
302
335
|
if (upload.kind === "walkthrough") {
|
|
303
336
|
const source = upload.snapshot.repository.source;
|
|
304
337
|
const repository = getRepositoryMetadata(source);
|
|
@@ -332,7 +365,7 @@ const uploadShare = async (request, env, options) => {
|
|
|
332
365
|
status: "uploaded",
|
|
333
366
|
updatedAt: now,
|
|
334
367
|
walkthroughSlug: slug
|
|
335
|
-
}).where(eq(uploadIntent.id, intent.id))]);
|
|
368
|
+
}).where(and(eq(uploadIntent.id, intent.id), eq(uploadIntent.status, "uploading")))]);
|
|
336
369
|
} else await db.batch([db.insert(plan).values({
|
|
337
370
|
agent: cleanIdentityValue(upload.snapshot.source?.agent, 40),
|
|
338
371
|
byteSize: textEncoder.encode(canonical).byteLength,
|
|
@@ -353,9 +386,16 @@ const uploadShare = async (request, env, options) => {
|
|
|
353
386
|
status: "uploaded",
|
|
354
387
|
updatedAt: now,
|
|
355
388
|
walkthroughSlug: slug
|
|
356
|
-
}).where(eq(uploadIntent.id, intent.id))]);
|
|
389
|
+
}).where(and(eq(uploadIntent.id, intent.id), eq(uploadIntent.status, "uploading")))]);
|
|
357
390
|
} catch (error) {
|
|
358
|
-
|
|
391
|
+
try {
|
|
392
|
+
await env.WALKTHROUGH_BUCKET.delete(objectKey);
|
|
393
|
+
} finally {
|
|
394
|
+
await db.update(uploadIntent).set({
|
|
395
|
+
status: "claimed",
|
|
396
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
397
|
+
}).where(and(eq(uploadIntent.id, intent.id), eq(uploadIntent.status, "uploading")));
|
|
398
|
+
}
|
|
359
399
|
if (isQuotaError(error)) return quotaResponse();
|
|
360
400
|
throw error;
|
|
361
401
|
}
|
|
@@ -400,4 +440,4 @@ const handleSharingApi = async (request, env, options) => {
|
|
|
400
440
|
};
|
|
401
441
|
const handleSharingApiRequest = (request, env, options = {}) => withDatabase(env.DB, () => handleSharingApi(request, env, options));
|
|
402
442
|
//#endregion
|
|
403
|
-
export { getAuthenticatedShareUser, getRepositoryMetadata, handleSharingApiRequest, parseGitPullRequestUrl, parseShareUpload, shareUserFromAuthValue };
|
|
443
|
+
export { getAuthenticatedShareUser, getRepositoryMetadata, handleSharingApiRequest, parseGitPullRequestUrl, parseShareUpload, readRequestText, shareUserFromAuthValue };
|
package/dist/fate.mjs
CHANGED
|
@@ -171,7 +171,7 @@ const indexWalkthroughFiles = async (database, walkthroughId, filePaths) => {
|
|
|
171
171
|
await database.update(walkthrough).set({ filesIndexedAt: /* @__PURE__ */ new Date() }).where(eq(walkthrough.id, walkthroughId));
|
|
172
172
|
};
|
|
173
173
|
const fetchShareStats = async (database, now = /* @__PURE__ */ new Date()) => {
|
|
174
|
-
const dayMilliseconds =
|
|
174
|
+
const dayMilliseconds = 864e5;
|
|
175
175
|
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
176
176
|
const start = /* @__PURE__ */ new Date(today.getTime() - 6 * dayMilliseconds);
|
|
177
177
|
const dayForPlan = sql$1`date(${plan.createdAt}, 'unixepoch')`;
|
|
@@ -209,12 +209,12 @@ const fetchShareStats = async (database, now = /* @__PURE__ */ new Date()) => {
|
|
|
209
209
|
totalWalkthroughs: walkthroughTotals[0]?.count ?? 0
|
|
210
210
|
};
|
|
211
211
|
};
|
|
212
|
-
const shareCommentBodySchema = z.string().trim().min(1).max(
|
|
212
|
+
const shareCommentBodySchema = z.string().trim().min(1).max(131072);
|
|
213
213
|
const shareCommentAnchorBlockSchema = z.object({
|
|
214
214
|
fingerprint: z.string().min(1).max(256),
|
|
215
215
|
path: z.array(z.number().int().min(0).max(1e6)).max(64),
|
|
216
216
|
runtimeKey: z.string().max(256).optional(),
|
|
217
|
-
text: z.string().max(
|
|
217
|
+
text: z.string().max(65536),
|
|
218
218
|
type: z.string().min(1).max(64)
|
|
219
219
|
});
|
|
220
220
|
const shareCommentAnchorSchema = z.object({
|
|
@@ -222,7 +222,7 @@ const shareCommentAnchorSchema = z.object({
|
|
|
222
222
|
kind: z.enum(["block", "text"]),
|
|
223
223
|
quote: z.object({
|
|
224
224
|
end: z.number().int().min(0).max(1e6),
|
|
225
|
-
exact: z.string().max(
|
|
225
|
+
exact: z.string().max(65536),
|
|
226
226
|
prefix: z.string().max(1024),
|
|
227
227
|
start: z.number().int().min(0).max(1e6),
|
|
228
228
|
suffix: z.string().max(1024)
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { plan, planRelations, shareCommentMessage, shareCommentMessageRelations, shareCommentThread, shareCommentThreadKind, shareCommentThreadRelations, shareCommentThreadStatus, shareDailyUsage, shareDailyUsageRelations, shareKind, uploadIntent, user, userRelations, walkthrough, walkthroughFile, walkthroughFileRelations, walkthroughRelations } from "./schema.mjs";
|
|
2
2
|
import { and, createDb, db, eq, gte, runDatabaseBatches, sql, withDatabase } from "./db.mjs";
|
|
3
|
-
import { getAuthenticatedShareUser, getRepositoryMetadata, handleSharingApiRequest, parseGitPullRequestUrl, parseShareUpload, shareUserFromAuthValue } from "./api.mjs";
|
|
3
|
+
import { getAuthenticatedShareUser, getRepositoryMetadata, handleSharingApiRequest, parseGitPullRequestUrl, parseShareUpload, readRequestText, shareUserFromAuthValue } from "./api.mjs";
|
|
4
4
|
import { SharingRoot, getSessionUserId, planDataView, shareCommentMessageDataView, shareCommentThreadDataView, shareStatsDataView, shareStatsDayDataView, uploadIntentDataView, uploadIntentSecretHeader, walkthroughDataView } from "./views.mjs";
|
|
5
5
|
import { createSharingFateServer, shareCommentAnchorSchema, shareCommentTargetSchema } from "./fate.mjs";
|
|
6
|
-
export { SharingRoot, and, createDb, createSharingFateServer, db, eq, getAuthenticatedShareUser, getRepositoryMetadata, getSessionUserId, gte, handleSharingApiRequest, parseGitPullRequestUrl, parseShareUpload, plan, planDataView, planRelations, runDatabaseBatches, shareCommentAnchorSchema, shareCommentMessage, shareCommentMessageDataView, shareCommentMessageRelations, shareCommentTargetSchema, shareCommentThread, shareCommentThreadDataView, shareCommentThreadKind, shareCommentThreadRelations, shareCommentThreadStatus, shareDailyUsage, shareDailyUsageRelations, shareKind, shareStatsDataView, shareStatsDayDataView, shareUserFromAuthValue, sql, uploadIntent, uploadIntentDataView, uploadIntentSecretHeader, user, userRelations, walkthrough, walkthroughDataView, walkthroughFile, walkthroughFileRelations, walkthroughRelations, withDatabase };
|
|
6
|
+
export { SharingRoot, and, createDb, createSharingFateServer, db, eq, getAuthenticatedShareUser, getRepositoryMetadata, getSessionUserId, gte, handleSharingApiRequest, parseGitPullRequestUrl, parseShareUpload, plan, planDataView, planRelations, readRequestText, runDatabaseBatches, shareCommentAnchorSchema, shareCommentMessage, shareCommentMessageDataView, shareCommentMessageRelations, shareCommentTargetSchema, shareCommentThread, shareCommentThreadDataView, shareCommentThreadKind, shareCommentThreadRelations, shareCommentThreadStatus, shareDailyUsage, shareDailyUsageRelations, shareKind, shareStatsDataView, shareStatsDayDataView, shareUserFromAuthValue, sql, uploadIntent, uploadIntentDataView, uploadIntentSecretHeader, user, userRelations, walkthrough, walkthroughDataView, walkthroughFile, walkthroughFileRelations, walkthroughRelations, withDatabase };
|
package/dist/schema.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nkzw/codiff-service",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "Provider-neutral Codiff sharing service.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@nkzw/fate": "^1.3.
|
|
66
|
+
"@nkzw/fate": "^1.3.3",
|
|
67
67
|
"drizzle-orm": "^0.45.2",
|
|
68
68
|
"zod": "^4.4.3",
|
|
69
|
-
"@nkzw/codiff-core": "1.
|
|
69
|
+
"@nkzw/codiff-core": "1.11.1"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
72
|
"react": "^19.2.0",
|
package/schema.ts
CHANGED
|
@@ -141,7 +141,7 @@ export const uploadIntent = sqliteTable(
|
|
|
141
141
|
secretHash: text().notNull(),
|
|
142
142
|
sharedByUserId: text().references(() => user.id, { onDelete: 'set null' }),
|
|
143
143
|
shareKind: text({ enum: shareKind }).notNull().default('walkthrough'),
|
|
144
|
-
status: text({ enum: ['pending', 'claimed', 'uploaded'] })
|
|
144
|
+
status: text({ enum: ['pending', 'claimed', 'uploading', 'uploaded'] })
|
|
145
145
|
.notNull()
|
|
146
146
|
.default('pending'),
|
|
147
147
|
updatedAt: timestampColumn(),
|