@copypatch/backend 2.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Efe Arabacı
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @copypatch/backend
2
+
3
+ Framework-neutral CopyPatch v2 backend runtime. It exposes a Web
4
+ `Request`/`Response` API that is mounted inside the host application, so
5
+ CopyPatch does not require a separate server, port, proxy, or CORS policy.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @copypatch/backend @copypatch/storage-sqlite
11
+ ```
12
+
13
+ Use `@copypatch/storage-postgres` instead when the application runs on
14
+ multiple instances or does not have persistent local disk.
15
+
16
+ ## Minimal usage
17
+
18
+ ```ts
19
+ import { createCopyPatchBackend } from '@copypatch/backend';
20
+ import { createSQLitePersistence } from '@copypatch/storage-sqlite';
21
+
22
+ const persistence = createSQLitePersistence('./data/copypatch.sqlite');
23
+ await persistence.migrate();
24
+
25
+ export const backend = createCopyPatchBackend({
26
+ persistence,
27
+ passphraseHash: process.env.COPYPATCH_PASSPHRASE_HASH!,
28
+ });
29
+
30
+ export const handle = (request: Request) => backend.handle(request);
31
+ ```
32
+
33
+ Mount `handle` at `\/__copypatch\/api\/v2` in the same deployment as the host
34
+ application. Unsafe requests require an exact same-origin `Origin` header.
35
+ CopyPatch does not emit CORS headers.
36
+
37
+ `passphraseHash` must be an Argon2id encoded hash. As an alternative, provide
38
+ a host `authAdapter`; the two authentication modes are mutually exclusive.
39
+ Host authentication context is passed as the second argument to `handle` and
40
+ must not be serialized into request headers.
41
+
42
+ For server rendering, `backend.readPublished(locale)` reads storage directly
43
+ and returns a safe public snapshot fallback if storage is temporarily
44
+ unavailable.
45
+
46
+ ## Exports
47
+
48
+ - `createCopyPatchBackend(options)`
49
+ - `CopyPatchBackend`
50
+ - `hashToken`, `hashRateLimitKey`, and token helpers
51
+ - `SESSION_COOKIE_NAME`
52
+
53
+ CopyPatch is ESM-only and requires Node.js 20 or newer.
54
+
55
+ ## License
56
+
57
+ MIT
@@ -0,0 +1,29 @@
1
+ import { type ContentSnapshot, type CopyPatchAuthAdapter, type CopyPatchHandleContext, type CopyPatchPersistence } from '@copypatch/core';
2
+ export declare const SESSION_COOKIE_NAME = "__Host-copypatch-session";
3
+ interface BackendBaseOptions {
4
+ persistence: CopyPatchPersistence;
5
+ maxTextLength?: number;
6
+ session?: {
7
+ idleTimeoutMs?: number;
8
+ absoluteTimeoutMs?: number;
9
+ };
10
+ rateLimit?: {
11
+ limit?: number;
12
+ windowMs?: number;
13
+ };
14
+ now?: () => number;
15
+ }
16
+ export type CopyPatchBackendOptions<THostAuth = unknown> = BackendBaseOptions & ({
17
+ passphraseHash: string;
18
+ authAdapter?: never;
19
+ } | {
20
+ authAdapter: CopyPatchAuthAdapter<THostAuth>;
21
+ passphraseHash?: never;
22
+ });
23
+ export interface CopyPatchBackend<THostAuth = unknown> {
24
+ handle(request: Request, context?: CopyPatchHandleContext<THostAuth>): Promise<Response>;
25
+ readPublished(locale: string): Promise<ContentSnapshot>;
26
+ }
27
+ export declare function createCopyPatchBackend<THostAuth = unknown>(options: CopyPatchBackendOptions<THostAuth>): CopyPatchBackend<THostAuth>;
28
+ export {};
29
+ //# sourceMappingURL=backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EAO1B,MAAM,iBAAiB,CAAC;AAGzB,eAAO,MAAM,mBAAmB,6BAA6B,CAAC;AAQ9D,UAAU,kBAAkB;IAC1B,WAAW,EAAE,oBAAoB,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE;QACR,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,SAAS,CAAC,EAAE;QACV,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,uBAAuB,CAAC,SAAS,GAAG,OAAO,IAAI,kBAAkB,GAAG,CAC5E;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/C;IAAE,WAAW,EAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAAC,cAAc,CAAC,EAAE,KAAK,CAAA;CAAE,CAC3E,CAAC;AAEF,MAAM,WAAW,gBAAgB,CAAC,SAAS,GAAG,OAAO;IACnD,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,sBAAsB,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzF,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACzD;AAsdD,wBAAgB,sBAAsB,CAAC,SAAS,GAAG,OAAO,EACxD,OAAO,EAAE,uBAAuB,CAAC,SAAS,CAAC,GAC1C,gBAAgB,CAAC,SAAS,CAAC,CAiC7B"}
@@ -0,0 +1,496 @@
1
+ import { API_BASE_PATH, CSRF_HEADER_NAME, DEFAULT_MAX_TEXT_LENGTH, HARD_MAX_TEXT_LENGTH, isValidContentKey, isValidLocale, normalizeText, } from '@copypatch/core';
2
+ import { generateToken, hashesEqual, hashRateLimitKey, hashToken, verifyPassphrase } from './crypto.js';
3
+ export const SESSION_COOKIE_NAME = '__Host-copypatch-session';
4
+ const UNSAFE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
5
+ const MAX_CHANGES = 500;
6
+ const MAX_BODY_BYTES = 2 * 1024 * 1024;
7
+ const DEFAULT_IDLE_TIMEOUT_MS = 12 * 60 * 60 * 1000;
8
+ const DEFAULT_ABSOLUTE_TIMEOUT_MS = 7 * 24 * 60 * 60 * 1000;
9
+ class CopyPatchBackendRuntime {
10
+ options;
11
+ snapshots = new Map();
12
+ constructor(options) {
13
+ this.options = options;
14
+ }
15
+ async readPublished(locale) {
16
+ if (!isValidLocale(locale))
17
+ throw new TypeError('Invalid locale.');
18
+ return (await this.readPublishedWithFallback(locale)).snapshot;
19
+ }
20
+ async handle(request, context = {}) {
21
+ try {
22
+ return this.secure(await this.route(request, context));
23
+ }
24
+ catch {
25
+ return this.secure(this.error(500, 'INTERNAL_ERROR', 'The request could not be completed.'));
26
+ }
27
+ }
28
+ async route(request, context) {
29
+ const url = new URL(request.url);
30
+ const method = request.method.toUpperCase();
31
+ if (method === 'OPTIONS')
32
+ return this.error(405, 'UNSUPPORTED_OPERATION', 'Method not allowed.');
33
+ if (UNSAFE_METHODS.has(method)) {
34
+ const originFailure = this.checkOrigin(request, url.origin);
35
+ if (originFailure)
36
+ return originFailure;
37
+ const rateFailure = await this.checkRateLimit(request, context, url.pathname);
38
+ if (rateFailure)
39
+ return rateFailure;
40
+ }
41
+ if (method === 'GET' && (url.pathname === '/healthz' || url.pathname === `${API_BASE_PATH}/health`)) {
42
+ const health = await this.options.persistence.health();
43
+ return this.json(health.ok ? 200 : 503, { status: health.ok ? 'ok' : 'unavailable' }, { 'cache-control': 'no-store' });
44
+ }
45
+ if (url.pathname === `${API_BASE_PATH}/session`) {
46
+ if (method === 'POST')
47
+ return this.createSession(request, context);
48
+ if (method === 'GET')
49
+ return this.readSession(request, context);
50
+ if (method === 'DELETE')
51
+ return this.deleteSession(request, context);
52
+ return this.error(405, 'UNSUPPORTED_OPERATION', 'Method not allowed.');
53
+ }
54
+ const contentMatch = this.matchLocale(url.pathname, `${API_BASE_PATH}/content/`);
55
+ if (contentMatch !== null) {
56
+ if (method !== 'GET')
57
+ return this.error(405, 'UNSUPPORTED_OPERATION', 'Method not allowed.');
58
+ if (!isValidLocale(contentMatch))
59
+ return this.validation('Invalid locale.');
60
+ const result = await this.readPublishedWithFallback(contentMatch);
61
+ const headers = result.fallback
62
+ ? { 'cache-control': 'no-store' }
63
+ : { 'cache-control': 'public, max-age=0, must-revalidate', etag: `W/"rev-${result.snapshot.revision}"` };
64
+ if (!result.fallback && request.headers.get('if-none-match') === headers.etag) {
65
+ return new Response(null, { status: 304, headers });
66
+ }
67
+ return this.json(200, result.snapshot, headers);
68
+ }
69
+ const changesMatch = this.matchLocale(url.pathname, `${API_BASE_PATH}/editor/`, '/changes');
70
+ if (changesMatch !== null) {
71
+ if (method !== 'PUT')
72
+ return this.error(405, 'UNSUPPORTED_OPERATION', 'Method not allowed.');
73
+ return this.saveDrafts(request, context, changesMatch);
74
+ }
75
+ const publishMatch = this.matchLocale(url.pathname, `${API_BASE_PATH}/editor/`, '/publish');
76
+ if (publishMatch !== null) {
77
+ if (method !== 'POST')
78
+ return this.error(405, 'UNSUPPORTED_OPERATION', 'Method not allowed.');
79
+ return this.publishDrafts(request, context, publishMatch);
80
+ }
81
+ const draftsMatch = this.matchLocale(url.pathname, `${API_BASE_PATH}/editor/`, '/drafts');
82
+ if (draftsMatch !== null) {
83
+ if (method !== 'DELETE')
84
+ return this.error(405, 'UNSUPPORTED_OPERATION', 'Method not allowed.');
85
+ return this.discardDrafts(request, context, draftsMatch);
86
+ }
87
+ const editorMatch = this.matchLocale(url.pathname, `${API_BASE_PATH}/editor/`);
88
+ if (editorMatch !== null) {
89
+ if (method !== 'GET')
90
+ return this.error(405, 'UNSUPPORTED_OPERATION', 'Method not allowed.');
91
+ if (!isValidLocale(editorMatch))
92
+ return this.validation('Invalid locale.');
93
+ const auth = await this.authenticate(request, context);
94
+ if (auth instanceof Response)
95
+ return auth;
96
+ if (!this.hasRole(auth.principal, 'editor') && !this.hasRole(auth.principal, 'publisher')) {
97
+ return this.error(403, 'UNAUTHENTICATED', 'Editor access is required.');
98
+ }
99
+ return this.json(200, await this.options.persistence.readEditor(editorMatch), { 'cache-control': 'no-store' });
100
+ }
101
+ return this.error(404, 'NOT_FOUND', 'Endpoint not found.');
102
+ }
103
+ async createSession(request, context) {
104
+ if (this.options.authAdapter) {
105
+ const auth = await this.authenticateHost(request, context, true);
106
+ if (auth instanceof Response)
107
+ return auth;
108
+ return this.json(200, { authenticated: true, requiresCsrf: false, roles: auth.principal.roles, publishingMode: 'draft' }, { 'cache-control': 'no-store' });
109
+ }
110
+ const parsed = await this.readJson(request);
111
+ if (parsed instanceof Response)
112
+ return parsed;
113
+ if (!this.isRecord(parsed))
114
+ return this.validation('Invalid session payload.');
115
+ const passphrase = parsed.passphrase ?? parsed.password;
116
+ if (typeof passphrase !== 'string' || passphrase.length < 1 || Object.keys(parsed).some((key) => key !== 'passphrase' && key !== 'password')) {
117
+ return this.validation('A passphrase is required.');
118
+ }
119
+ if (!await verifyPassphrase(passphrase, this.options.passphraseHash ?? '')) {
120
+ return this.error(401, 'UNAUTHENTICATED', 'Invalid credentials.');
121
+ }
122
+ const now = this.options.now();
123
+ const sessionToken = generateToken();
124
+ const csrfToken = generateToken();
125
+ const stored = {
126
+ tokenHash: hashToken(sessionToken),
127
+ csrfTokenHash: hashToken(csrfToken),
128
+ subject: 'built-in',
129
+ roles: ['editor', 'publisher'],
130
+ createdAt: now,
131
+ lastSeenAt: now,
132
+ idleExpiresAt: now + this.options.idleTimeoutMs,
133
+ absoluteExpiresAt: now + this.options.absoluteTimeoutMs,
134
+ };
135
+ await this.options.persistence.createSession(stored);
136
+ const cookie = `${SESSION_COOKIE_NAME}=${sessionToken}; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=${Math.floor(this.options.absoluteTimeoutMs / 1000)}`;
137
+ return this.json(200, { authenticated: true, requiresCsrf: true, csrfToken, roles: stored.roles, publishingMode: 'draft' }, {
138
+ 'cache-control': 'no-store',
139
+ 'set-cookie': cookie,
140
+ });
141
+ }
142
+ async readSession(request, context) {
143
+ if (this.options.authAdapter) {
144
+ const auth = await this.authenticateHost(request, context, false);
145
+ if (auth instanceof Response)
146
+ return this.json(200, { authenticated: false, requiresCsrf: false }, { 'cache-control': 'no-store' });
147
+ return this.json(200, { authenticated: true, requiresCsrf: false, roles: auth.principal.roles, publishingMode: 'draft' }, { 'cache-control': 'no-store' });
148
+ }
149
+ const auth = await this.authenticateBuiltIn(request);
150
+ if (auth instanceof Response)
151
+ return this.json(200, { authenticated: false, requiresCsrf: false }, { 'cache-control': 'no-store' });
152
+ const csrfToken = generateToken();
153
+ const updated = await this.options.persistence.touchSession(auth.sessionTokenHash, {
154
+ lastSeenAt: this.options.now(),
155
+ idleExpiresAt: Math.min(this.options.now() + this.options.idleTimeoutMs, auth.session.absoluteExpiresAt),
156
+ csrfTokenHash: hashToken(csrfToken),
157
+ });
158
+ if (!updated)
159
+ return this.json(200, { authenticated: false, requiresCsrf: false }, { 'cache-control': 'no-store' });
160
+ return this.json(200, { authenticated: true, requiresCsrf: true, csrfToken, roles: updated.roles, publishingMode: 'draft' }, { 'cache-control': 'no-store' });
161
+ }
162
+ async deleteSession(request, context) {
163
+ const auth = await this.authenticateMutation(request, context, 'editor');
164
+ if (auth instanceof Response)
165
+ return auth;
166
+ if (auth.sessionTokenHash)
167
+ await this.options.persistence.deleteSession(auth.sessionTokenHash);
168
+ const expiredCookie = `${SESSION_COOKIE_NAME}=; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=0`;
169
+ return this.json(200, { success: true }, { 'cache-control': 'no-store', 'set-cookie': expiredCookie });
170
+ }
171
+ async saveDrafts(request, context, locale) {
172
+ if (!isValidLocale(locale))
173
+ return this.validation('Invalid locale.');
174
+ const auth = await this.authenticateMutation(request, context, 'editor');
175
+ if (auth instanceof Response)
176
+ return auth;
177
+ const parsed = await this.readJson(request);
178
+ if (parsed instanceof Response)
179
+ return parsed;
180
+ const command = this.parseSaveCommand(parsed, locale);
181
+ if (command instanceof Response)
182
+ return command;
183
+ const result = await this.options.persistence.saveDrafts(command);
184
+ return this.mutationResponse(result);
185
+ }
186
+ async publishDrafts(request, context, locale) {
187
+ if (!isValidLocale(locale))
188
+ return this.validation('Invalid locale.');
189
+ const auth = await this.authenticateMutation(request, context, 'publisher');
190
+ if (auth instanceof Response)
191
+ return auth;
192
+ const parsed = await this.readJson(request);
193
+ if (parsed instanceof Response)
194
+ return parsed;
195
+ const revisions = this.parseRevisions(parsed);
196
+ if (revisions instanceof Response)
197
+ return revisions;
198
+ const result = await this.options.persistence.publishDrafts({ locale, ...revisions });
199
+ return this.mutationResponse(result);
200
+ }
201
+ async discardDrafts(request, context, locale) {
202
+ if (!isValidLocale(locale))
203
+ return this.validation('Invalid locale.');
204
+ const auth = await this.authenticateMutation(request, context, 'editor');
205
+ if (auth instanceof Response)
206
+ return auth;
207
+ const parsed = await this.readJson(request);
208
+ if (parsed instanceof Response)
209
+ return parsed;
210
+ const revisions = this.parseRevisions(parsed);
211
+ if (revisions instanceof Response)
212
+ return revisions;
213
+ const result = await this.options.persistence.discardDrafts({ locale, ...revisions });
214
+ return this.mutationResponse(result);
215
+ }
216
+ async authenticateMutation(request, context, role) {
217
+ const auth = this.options.authAdapter
218
+ ? await this.authenticateHost(request, context, true)
219
+ : await this.authenticateBuiltIn(request);
220
+ if (auth instanceof Response)
221
+ return auth;
222
+ if (!this.hasRole(auth.principal, role))
223
+ return this.error(403, 'UNAUTHENTICATED', `${role} role is required.`);
224
+ if (!this.options.authAdapter) {
225
+ const csrf = request.headers.get(CSRF_HEADER_NAME);
226
+ if (!csrf || !auth.session || !hashesEqual(hashToken(csrf), auth.session.csrfTokenHash)) {
227
+ return this.error(403, 'CSRF_FAILED', 'Mutation verification failed.');
228
+ }
229
+ }
230
+ return auth;
231
+ }
232
+ async authenticate(request, context) {
233
+ return this.options.authAdapter
234
+ ? this.authenticateHost(request, context, false)
235
+ : this.authenticateBuiltIn(request);
236
+ }
237
+ async authenticateHost(request, context, mutation) {
238
+ try {
239
+ const principal = await this.options.authAdapter.authenticate(request.clone(), context);
240
+ if (!principal || !this.validPrincipal(principal))
241
+ return this.error(401, 'UNAUTHENTICATED', 'Authentication is required.');
242
+ if (mutation) {
243
+ const verified = await this.options.authAdapter.verifyMutation(request.clone(), principal, context);
244
+ if (verified === false)
245
+ return this.error(403, 'CSRF_FAILED', 'Mutation verification failed.');
246
+ }
247
+ return { principal };
248
+ }
249
+ catch {
250
+ return this.error(mutation ? 403 : 401, mutation ? 'CSRF_FAILED' : 'UNAUTHENTICATED', mutation ? 'Mutation verification failed.' : 'Authentication is required.');
251
+ }
252
+ }
253
+ async authenticateBuiltIn(request) {
254
+ const rawToken = this.cookie(request, SESSION_COOKIE_NAME);
255
+ if (!rawToken)
256
+ return this.error(401, 'UNAUTHENTICATED', 'Authentication is required.');
257
+ const tokenHash = hashToken(rawToken);
258
+ const session = await this.options.persistence.readSession(tokenHash);
259
+ const now = this.options.now();
260
+ if (!session || session.idleExpiresAt <= now || session.absoluteExpiresAt <= now || !this.validPrincipal(session)) {
261
+ if (session)
262
+ await this.options.persistence.deleteSession(tokenHash);
263
+ return this.error(401, 'UNAUTHENTICATED', 'Authentication is required.');
264
+ }
265
+ const updated = await this.options.persistence.touchSession(tokenHash, {
266
+ lastSeenAt: now,
267
+ idleExpiresAt: Math.min(now + this.options.idleTimeoutMs, session.absoluteExpiresAt),
268
+ });
269
+ if (!updated)
270
+ return this.error(401, 'UNAUTHENTICATED', 'Authentication is required.');
271
+ return { principal: updated, sessionTokenHash: tokenHash, session: updated };
272
+ }
273
+ async checkRateLimit(request, context, pathname) {
274
+ const identity = context.clientAddress ?? 'unknown-client';
275
+ const keyHash = hashRateLimitKey(`${identity}\0${request.method.toUpperCase()}\0${pathname}`);
276
+ const decision = await this.options.persistence.consumeRateLimit({
277
+ keyHash,
278
+ limit: this.options.rateLimit,
279
+ windowMs: this.options.rateLimitWindowMs,
280
+ now: this.options.now(),
281
+ });
282
+ if (decision.allowed)
283
+ return null;
284
+ return this.json(429, {
285
+ error: { code: 'RATE_LIMITED', message: 'Too many requests.' },
286
+ }, {
287
+ 'cache-control': 'no-store',
288
+ 'retry-after': String(Math.max(1, Math.ceil((decision.resetAt - this.options.now()) / 1000))),
289
+ });
290
+ }
291
+ checkOrigin(request, expectedOrigin) {
292
+ const origin = request.headers.get('origin');
293
+ if (origin !== expectedOrigin)
294
+ return this.error(403, 'ORIGIN_REJECTED', 'Exact same-origin is required.');
295
+ return null;
296
+ }
297
+ parseSaveCommand(value, locale) {
298
+ if (!this.isRecord(value) || !this.onlyKeys(value, ['expectedPublishedRevision', 'expectedDraftRevision', 'changes'])) {
299
+ return this.validation('Invalid draft payload.');
300
+ }
301
+ const revisions = this.parseRevisionValues(value.expectedPublishedRevision, value.expectedDraftRevision);
302
+ if (!revisions || !Array.isArray(value.changes) || value.changes.length < 1 || value.changes.length > MAX_CHANGES) {
303
+ return this.validation('Invalid draft payload.');
304
+ }
305
+ const keys = new Set();
306
+ const changes = [];
307
+ for (const item of value.changes) {
308
+ if (!this.isRecord(item) || !this.onlyKeys(item, ['key', 'text']) || typeof item.key !== 'string' || typeof item.text !== 'string') {
309
+ return this.validation('Invalid content change.');
310
+ }
311
+ if (!isValidContentKey(item.key) || item.text.length > this.options.maxTextLength || keys.has(item.key)) {
312
+ return this.validation('Invalid content change.');
313
+ }
314
+ keys.add(item.key);
315
+ changes.push({ key: item.key, text: normalizeText(item.text, true) });
316
+ }
317
+ return { locale, ...revisions, changes };
318
+ }
319
+ parseRevisions(value) {
320
+ if (!this.isRecord(value) || !this.onlyKeys(value, ['expectedPublishedRevision', 'expectedDraftRevision'])) {
321
+ return this.validation('Both expected revisions are required.');
322
+ }
323
+ const revisions = this.parseRevisionValues(value.expectedPublishedRevision, value.expectedDraftRevision);
324
+ return revisions ?? this.validation('Both expected revisions must be positive safe integers.');
325
+ }
326
+ parseRevisionValues(published, draft) {
327
+ if (!Number.isSafeInteger(published) || !Number.isSafeInteger(draft) || published < 1 || draft < 1)
328
+ return null;
329
+ return { expectedPublishedRevision: published, expectedDraftRevision: draft };
330
+ }
331
+ mutationResponse(result) {
332
+ if (result.status === 'conflict') {
333
+ return this.json(409, {
334
+ error: { code: 'REVISION_CONFLICT', message: 'The editor snapshot has changed.' },
335
+ latest: result.latest,
336
+ }, { 'cache-control': 'no-store' });
337
+ }
338
+ return this.json(200, { success: true, ...result.value }, { 'cache-control': 'no-store' });
339
+ }
340
+ async readPublishedWithFallback(locale) {
341
+ try {
342
+ const snapshot = this.copySnapshot(await this.options.persistence.readPublished(locale));
343
+ this.snapshots.set(locale, snapshot);
344
+ return { snapshot: this.copySnapshot(snapshot), fallback: false };
345
+ }
346
+ catch {
347
+ const cached = this.snapshots.get(locale) ?? { revision: 1, content: {} };
348
+ return { snapshot: this.copySnapshot(cached), fallback: true };
349
+ }
350
+ }
351
+ copySnapshot(snapshot) {
352
+ return { revision: snapshot.revision, content: { ...snapshot.content } };
353
+ }
354
+ async readJson(request) {
355
+ const contentLength = Number(request.headers.get('content-length') ?? '0');
356
+ if (Number.isFinite(contentLength) && contentLength > MAX_BODY_BYTES)
357
+ return this.validation('Request body is too large.');
358
+ let text;
359
+ try {
360
+ const body = await this.readTextBody(request);
361
+ if (body instanceof Response)
362
+ return body;
363
+ text = body;
364
+ }
365
+ catch {
366
+ return this.validation('Request body could not be read.');
367
+ }
368
+ if (Buffer.byteLength(text, 'utf8') > MAX_BODY_BYTES)
369
+ return this.validation('Request body is too large.');
370
+ try {
371
+ return JSON.parse(text);
372
+ }
373
+ catch {
374
+ return this.validation('Invalid JSON request body.');
375
+ }
376
+ }
377
+ async readTextBody(request) {
378
+ if (!request.body)
379
+ return '';
380
+ const reader = request.body.getReader();
381
+ const chunks = [];
382
+ let byteLength = 0;
383
+ try {
384
+ while (true) {
385
+ const { done, value } = await reader.read();
386
+ if (done)
387
+ break;
388
+ if (!value)
389
+ continue;
390
+ byteLength += value.byteLength;
391
+ if (byteLength > MAX_BODY_BYTES) {
392
+ await reader.cancel().catch(() => undefined);
393
+ return this.validation('Request body is too large.');
394
+ }
395
+ chunks.push(value);
396
+ }
397
+ }
398
+ finally {
399
+ reader.releaseLock();
400
+ }
401
+ return Buffer.concat(chunks, byteLength).toString('utf8');
402
+ }
403
+ matchLocale(pathname, prefix, suffix = '') {
404
+ if (!pathname.startsWith(prefix) || (suffix && !pathname.endsWith(suffix)))
405
+ return null;
406
+ const end = suffix ? pathname.length - suffix.length : pathname.length;
407
+ const encoded = pathname.slice(prefix.length, end);
408
+ if (!encoded || encoded.includes('/'))
409
+ return null;
410
+ try {
411
+ return decodeURIComponent(encoded);
412
+ }
413
+ catch {
414
+ return '';
415
+ }
416
+ }
417
+ cookie(request, name) {
418
+ for (const part of (request.headers.get('cookie') ?? '').split(';')) {
419
+ const separator = part.indexOf('=');
420
+ if (separator < 0)
421
+ continue;
422
+ if (part.slice(0, separator).trim() === name)
423
+ return part.slice(separator + 1).trim();
424
+ }
425
+ return null;
426
+ }
427
+ validPrincipal(principal) {
428
+ return typeof principal.subject === 'string'
429
+ && principal.subject.length > 0
430
+ && Array.isArray(principal.roles)
431
+ && principal.roles.every((role) => role === 'editor' || role === 'publisher');
432
+ }
433
+ hasRole(principal, role) {
434
+ return principal.roles.includes(role);
435
+ }
436
+ isRecord(value) {
437
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
438
+ }
439
+ onlyKeys(value, allowed) {
440
+ return Object.keys(value).every((key) => allowed.includes(key));
441
+ }
442
+ secure(response) {
443
+ response.headers.set('x-content-type-options', 'nosniff');
444
+ response.headers.set('x-frame-options', 'SAMEORIGIN');
445
+ response.headers.set('referrer-policy', 'same-origin');
446
+ return response;
447
+ }
448
+ validation(message) {
449
+ return this.error(400, 'VALIDATION_ERROR', message);
450
+ }
451
+ error(status, code, message) {
452
+ const body = { error: { code, message } };
453
+ return this.json(status, body, { 'cache-control': 'no-store' });
454
+ }
455
+ json(status, value, headers = {}) {
456
+ return Response.json(value, { status, headers });
457
+ }
458
+ }
459
+ export function createCopyPatchBackend(options) {
460
+ const hasPassphrase = typeof options.passphraseHash === 'string';
461
+ const hasAdapter = typeof options.authAdapter === 'object';
462
+ if (hasPassphrase === hasAdapter)
463
+ throw new TypeError('Configure exactly one of passphraseHash or authAdapter.');
464
+ if (hasPassphrase && !options.passphraseHash.startsWith('$argon2id$')) {
465
+ throw new TypeError('passphraseHash must be an Argon2id encoded hash.');
466
+ }
467
+ const maxTextLength = options.maxTextLength ?? DEFAULT_MAX_TEXT_LENGTH;
468
+ if (!Number.isSafeInteger(maxTextLength) || maxTextLength < 1 || maxTextLength > HARD_MAX_TEXT_LENGTH) {
469
+ throw new TypeError(`maxTextLength must be between 1 and ${HARD_MAX_TEXT_LENGTH}.`);
470
+ }
471
+ const idleTimeoutMs = options.session?.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
472
+ const absoluteTimeoutMs = options.session?.absoluteTimeoutMs ?? DEFAULT_ABSOLUTE_TIMEOUT_MS;
473
+ const rateLimit = options.rateLimit?.limit ?? 30;
474
+ const rateLimitWindowMs = options.rateLimit?.windowMs ?? 60_000;
475
+ for (const [name, value] of Object.entries({ idleTimeoutMs, absoluteTimeoutMs, rateLimit, rateLimitWindowMs })) {
476
+ if (!Number.isSafeInteger(value) || value < 1)
477
+ throw new TypeError(`${name} must be a positive safe integer.`);
478
+ }
479
+ if (idleTimeoutMs > absoluteTimeoutMs)
480
+ throw new TypeError('idleTimeoutMs cannot exceed absoluteTimeoutMs.');
481
+ const resolved = {
482
+ persistence: options.persistence,
483
+ maxTextLength,
484
+ idleTimeoutMs,
485
+ absoluteTimeoutMs,
486
+ rateLimit,
487
+ rateLimitWindowMs,
488
+ now: options.now ?? Date.now,
489
+ };
490
+ if (hasPassphrase)
491
+ resolved.passphraseHash = options.passphraseHash;
492
+ if (hasAdapter)
493
+ resolved.authAdapter = options.authAdapter;
494
+ return new CopyPatchBackendRuntime(resolved);
495
+ }
496
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function generateToken(): string;
2
+ export declare function hashToken(token: string): string;
3
+ export declare function hashRateLimitKey(value: string): string;
4
+ export declare function hashesEqual(left: string, right: string): boolean;
5
+ export declare function verifyPassphrase(passphrase: string, argon2idHash: string): Promise<boolean>;
6
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAIhE;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOjG"}
package/dist/crypto.js ADDED
@@ -0,0 +1,27 @@
1
+ import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ import { verify } from '@node-rs/argon2';
3
+ export function generateToken() {
4
+ return randomBytes(32).toString('base64url');
5
+ }
6
+ export function hashToken(token) {
7
+ return createHash('sha256').update(token, 'utf8').digest('hex');
8
+ }
9
+ export function hashRateLimitKey(value) {
10
+ return hashToken(`copypatch-rate-limit\0${value}`);
11
+ }
12
+ export function hashesEqual(left, right) {
13
+ const leftBytes = Buffer.from(left, 'hex');
14
+ const rightBytes = Buffer.from(right, 'hex');
15
+ return leftBytes.length === rightBytes.length && timingSafeEqual(leftBytes, rightBytes);
16
+ }
17
+ export async function verifyPassphrase(passphrase, argon2idHash) {
18
+ if (!argon2idHash.startsWith('$argon2id$'))
19
+ return false;
20
+ try {
21
+ return await verify(argon2idHash, passphrase);
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,SAAS,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAa;IACrD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC7C,OAAO,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,IAAI,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAkB,EAAE,YAAoB;IAC7E,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './backend.js';
2
+ export * from './crypto.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './backend.js';
2
+ export * from './crypto.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@copypatch/backend",
3
+ "version": "2.0.0",
4
+ "description": "Storage-independent CopyPatch backend runtime",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "engines": {
21
+ "node": ">=20.0.0"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "registry": "https://registry.npmjs.org/"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -b tsconfig.json",
29
+ "typecheck": "tsc --noEmit",
30
+ "clean": "rm -rf dist"
31
+ },
32
+ "dependencies": {
33
+ "@copypatch/core": "2.0.0",
34
+ "@node-rs/argon2": "^2.0.2"
35
+ },
36
+ "devDependencies": {
37
+ "typescript": "^5.8.0"
38
+ }
39
+ }