dineway 0.1.3

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 (96) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +89 -0
  3. package/dist/adapters-BlzWJG82.d.mts +106 -0
  4. package/dist/apply-CAPvMfoU.mjs +1339 -0
  5. package/dist/astro/index.d.mts +50 -0
  6. package/dist/astro/index.mjs +1326 -0
  7. package/dist/astro/middleware/auth.d.mts +30 -0
  8. package/dist/astro/middleware/auth.mjs +708 -0
  9. package/dist/astro/middleware/redirect.d.mts +21 -0
  10. package/dist/astro/middleware/redirect.mjs +62 -0
  11. package/dist/astro/middleware/request-context.d.mts +17 -0
  12. package/dist/astro/middleware/request-context.mjs +1371 -0
  13. package/dist/astro/middleware/setup.d.mts +19 -0
  14. package/dist/astro/middleware/setup.mjs +46 -0
  15. package/dist/astro/middleware.d.mts +12 -0
  16. package/dist/astro/middleware.mjs +1716 -0
  17. package/dist/astro/types.d.mts +269 -0
  18. package/dist/astro/types.mjs +1 -0
  19. package/dist/base64-F8-DUraK.mjs +58 -0
  20. package/dist/byline-DeWCMU_i.mjs +234 -0
  21. package/dist/bylines-DyqBV9EQ.mjs +137 -0
  22. package/dist/chunk-ClPoSABd.mjs +21 -0
  23. package/dist/cli/index.d.mts +1 -0
  24. package/dist/cli/index.mjs +3987 -0
  25. package/dist/client/external-auth-headers.d.mts +38 -0
  26. package/dist/client/external-auth-headers.mjs +101 -0
  27. package/dist/client/index.d.mts +397 -0
  28. package/dist/client/index.mjs +345 -0
  29. package/dist/config-Cq8H0SfX.mjs +46 -0
  30. package/dist/connection-C9pxzuag.mjs +52 -0
  31. package/dist/content-zSgdNmnt.mjs +836 -0
  32. package/dist/db/index.d.mts +4 -0
  33. package/dist/db/index.mjs +62 -0
  34. package/dist/db/libsql.d.mts +10 -0
  35. package/dist/db/libsql.mjs +21 -0
  36. package/dist/db/postgres.d.mts +10 -0
  37. package/dist/db/postgres.mjs +29 -0
  38. package/dist/db/sqlite.d.mts +10 -0
  39. package/dist/db/sqlite.mjs +15 -0
  40. package/dist/default-WYlzADZL.mjs +80 -0
  41. package/dist/dialect-helpers-B9uSp2GJ.mjs +89 -0
  42. package/dist/error-DrxtnGPg.mjs +26 -0
  43. package/dist/index-C-jx21qs.d.mts +4771 -0
  44. package/dist/index.d.mts +16 -0
  45. package/dist/index.mjs +30 -0
  46. package/dist/load-C6FCD1FU.mjs +27 -0
  47. package/dist/loader-qKmo0wAY.mjs +446 -0
  48. package/dist/manifest-schema-CTSEyIJ3.mjs +186 -0
  49. package/dist/media/index.d.mts +25 -0
  50. package/dist/media/index.mjs +54 -0
  51. package/dist/media/local-runtime.d.mts +38 -0
  52. package/dist/media/local-runtime.mjs +132 -0
  53. package/dist/media-DMTr80Gv.mjs +199 -0
  54. package/dist/mode-BlyYtIFO.mjs +22 -0
  55. package/dist/page/index.d.mts +148 -0
  56. package/dist/page/index.mjs +419 -0
  57. package/dist/placeholder-B3knXwNc.mjs +267 -0
  58. package/dist/placeholder-bOx1xCTY.d.mts +283 -0
  59. package/dist/plugin-utils.d.mts +57 -0
  60. package/dist/plugin-utils.mjs +77 -0
  61. package/dist/plugins/adapt-sandbox-entry.d.mts +21 -0
  62. package/dist/plugins/adapt-sandbox-entry.mjs +112 -0
  63. package/dist/query-BiaPl_g2.mjs +459 -0
  64. package/dist/redirect-JPqLAbxa.mjs +328 -0
  65. package/dist/registry-DSd1GWB8.mjs +851 -0
  66. package/dist/request-context.d.mts +49 -0
  67. package/dist/request-context.mjs +42 -0
  68. package/dist/runner-B5l1JfOj.d.mts +26 -0
  69. package/dist/runner-BGUGywgG.mjs +1529 -0
  70. package/dist/runtime.d.mts +25 -0
  71. package/dist/runtime.mjs +41 -0
  72. package/dist/search-BNruJHDL.mjs +11054 -0
  73. package/dist/seed/index.d.mts +3 -0
  74. package/dist/seed/index.mjs +15 -0
  75. package/dist/seo/index.d.mts +69 -0
  76. package/dist/seo/index.mjs +69 -0
  77. package/dist/storage/local.d.mts +38 -0
  78. package/dist/storage/local.mjs +165 -0
  79. package/dist/storage/s3.d.mts +31 -0
  80. package/dist/storage/s3.mjs +174 -0
  81. package/dist/tokens-4vgYuXsZ.mjs +170 -0
  82. package/dist/transport-C5FYnid7.mjs +417 -0
  83. package/dist/transport-gIL-e43D.d.mts +41 -0
  84. package/dist/types-BawVha09.mjs +30 -0
  85. package/dist/types-BgQeVaPj.d.mts +192 -0
  86. package/dist/types-CLLdsG3g.d.mts +103 -0
  87. package/dist/types-D38djUXv.d.mts +1196 -0
  88. package/dist/types-DShnjzb6.mjs +15 -0
  89. package/dist/types-DkvMXalq.d.mts +425 -0
  90. package/dist/types-DuNbGKjF.mjs +74 -0
  91. package/dist/types-ju-_ORz7.d.mts +182 -0
  92. package/dist/validate-CXnRKfJK.mjs +327 -0
  93. package/dist/validate-CqRJb_xU.mjs +96 -0
  94. package/dist/validate-DVKJJ-M_.d.mts +377 -0
  95. package/locals.d.ts +47 -0
  96. package/package.json +313 -0
@@ -0,0 +1,170 @@
1
+ import { i as encodeBase64url, n as decodeBase64url } from "./base64-F8-DUraK.mjs";
2
+
3
+ //#region src/preview/tokens.ts
4
+ /**
5
+ * Preview token generation and verification
6
+ *
7
+ * Tokens are compact, URL-safe, and HMAC-signed.
8
+ * Format: base64url(JSON payload).base64url(HMAC signature)
9
+ *
10
+ * Payload: { cid: contentId, exp: expiryTimestamp, iat: issuedAt }
11
+ */
12
+ const DURATION_PATTERN = /^(\d+)([smhdw])$/;
13
+ /**
14
+ * Parse duration string to seconds
15
+ * Supports: "1h", "30m", "1d", "2w", or raw seconds
16
+ */
17
+ function parseDuration(duration) {
18
+ if (typeof duration === "number") return duration;
19
+ const match = duration.match(DURATION_PATTERN);
20
+ if (!match) throw new Error(`Invalid duration format: "${duration}". Use "1h", "30m", "1d", "2w", or seconds.`);
21
+ const value = parseInt(match[1], 10);
22
+ const unit = match[2];
23
+ switch (unit) {
24
+ case "s": return value;
25
+ case "m": return value * 60;
26
+ case "h": return value * 60 * 60;
27
+ case "d": return value * 60 * 60 * 24;
28
+ case "w": return value * 60 * 60 * 24 * 7;
29
+ default: throw new Error(`Unknown duration unit: ${unit}`);
30
+ }
31
+ }
32
+ /**
33
+ * Create HMAC-SHA256 signature using Web Crypto API
34
+ */
35
+ async function createSignature(data, secret) {
36
+ const encoder = new TextEncoder();
37
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
38
+ name: "HMAC",
39
+ hash: "SHA-256"
40
+ }, false, ["sign"]);
41
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
42
+ return new Uint8Array(signature);
43
+ }
44
+ /**
45
+ * Verify HMAC-SHA256 signature
46
+ */
47
+ async function verifySignature(data, signature, secret) {
48
+ const encoder = new TextEncoder();
49
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
50
+ name: "HMAC",
51
+ hash: "SHA-256"
52
+ }, false, ["verify"]);
53
+ const sigBuffer = new ArrayBuffer(signature.byteLength);
54
+ new Uint8Array(sigBuffer).set(signature);
55
+ return crypto.subtle.verify("HMAC", key, sigBuffer, encoder.encode(data));
56
+ }
57
+ /**
58
+ * Generate a preview token for content
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const token = await generatePreviewToken({
63
+ * contentId: "posts:abc123",
64
+ * expiresIn: "1h",
65
+ * secret: process.env.PREVIEW_SECRET!,
66
+ * });
67
+ * ```
68
+ */
69
+ async function generatePreviewToken(options) {
70
+ const { contentId, expiresIn = "1h", secret } = options;
71
+ if (!secret) throw new Error("Preview secret is required");
72
+ if (!contentId || !contentId.includes(":")) throw new Error("Content ID must be in format \"collection:id\"");
73
+ const now = Math.floor(Date.now() / 1e3);
74
+ const payload = {
75
+ cid: contentId,
76
+ exp: now + parseDuration(expiresIn),
77
+ iat: now
78
+ };
79
+ const payloadJson = JSON.stringify(payload);
80
+ const encodedPayload = encodeBase64url(new TextEncoder().encode(payloadJson));
81
+ return `${encodedPayload}.${encodeBase64url(await createSignature(encodedPayload, secret))}`;
82
+ }
83
+ /**
84
+ * Verify a preview token and return the payload
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * // With URL (extracts _preview query param)
89
+ * const result = await verifyPreviewToken({
90
+ * url: Astro.url,
91
+ * secret: import.meta.env.PREVIEW_SECRET,
92
+ * });
93
+ *
94
+ * // With token directly
95
+ * const result = await verifyPreviewToken({
96
+ * token: someToken,
97
+ * secret: import.meta.env.PREVIEW_SECRET,
98
+ * });
99
+ *
100
+ * if (result.valid) {
101
+ * console.log(result.payload.cid); // "posts:abc123"
102
+ * }
103
+ * ```
104
+ */
105
+ async function verifyPreviewToken(options) {
106
+ const { secret } = options;
107
+ if (!secret) throw new Error("Preview secret is required");
108
+ const token = "url" in options ? options.url.searchParams.get("_preview") : options.token;
109
+ if (!token) return {
110
+ valid: false,
111
+ error: "none"
112
+ };
113
+ const parts = token.split(".");
114
+ if (parts.length !== 2) return {
115
+ valid: false,
116
+ error: "malformed"
117
+ };
118
+ const [encodedPayload, encodedSignature] = parts;
119
+ let signature;
120
+ try {
121
+ signature = decodeBase64url(encodedSignature);
122
+ } catch {
123
+ return {
124
+ valid: false,
125
+ error: "malformed"
126
+ };
127
+ }
128
+ if (!await verifySignature(encodedPayload, signature, secret)) return {
129
+ valid: false,
130
+ error: "invalid"
131
+ };
132
+ let payload;
133
+ try {
134
+ const payloadBytes = decodeBase64url(encodedPayload);
135
+ const payloadJson = new TextDecoder().decode(payloadBytes);
136
+ payload = JSON.parse(payloadJson);
137
+ } catch {
138
+ return {
139
+ valid: false,
140
+ error: "malformed"
141
+ };
142
+ }
143
+ if (typeof payload.cid !== "string" || typeof payload.exp !== "number" || typeof payload.iat !== "number") return {
144
+ valid: false,
145
+ error: "malformed"
146
+ };
147
+ const now = Math.floor(Date.now() / 1e3);
148
+ if (payload.exp < now) return {
149
+ valid: false,
150
+ error: "expired"
151
+ };
152
+ return {
153
+ valid: true,
154
+ payload
155
+ };
156
+ }
157
+ /**
158
+ * Parse a content ID into collection and id
159
+ */
160
+ function parseContentId(contentId) {
161
+ const colonIndex = contentId.indexOf(":");
162
+ if (colonIndex === -1) throw new Error("Content ID must be in format \"collection:id\"");
163
+ return {
164
+ collection: contentId.slice(0, colonIndex),
165
+ id: contentId.slice(colonIndex + 1)
166
+ };
167
+ }
168
+
169
+ //#endregion
170
+ export { parseContentId as n, verifyPreviewToken as r, generatePreviewToken as t };
@@ -0,0 +1,417 @@
1
+ //#region src/client/portable-text.ts
2
+ /**
3
+ * Convert Portable Text blocks to Markdown.
4
+ * Unknown block types are serialized as opaque fences.
5
+ */
6
+ function portableTextToMarkdown(blocks) {
7
+ const lines = [];
8
+ let prevWasList = false;
9
+ for (let i = 0; i < blocks.length; i++) {
10
+ const block = blocks[i];
11
+ if (block._type === "block") {
12
+ const isList = !!block.listItem;
13
+ if (i > 0 && (!isList || !prevWasList)) lines.push("");
14
+ lines.push(renderStandardBlock(block));
15
+ prevWasList = isList;
16
+ } else if (block._type === "code") {
17
+ if (i > 0) lines.push("");
18
+ const lang = block.language || "";
19
+ const code = block.code || "";
20
+ lines.push("```" + lang);
21
+ lines.push(code);
22
+ lines.push("```");
23
+ prevWasList = false;
24
+ } else if (block._type === "image") {
25
+ if (i > 0) lines.push("");
26
+ const alt = block.alt || "";
27
+ const url = block.asset?.url || "";
28
+ lines.push(`![${alt}](${url})`);
29
+ prevWasList = false;
30
+ } else {
31
+ if (i > 0) lines.push("");
32
+ lines.push(`<!--ec:block ${JSON.stringify(block)} -->`);
33
+ prevWasList = false;
34
+ }
35
+ }
36
+ return lines.join("\n") + "\n";
37
+ }
38
+ function renderStandardBlock(block) {
39
+ const text = renderSpans(block.children ?? [], block.markDefs ?? []);
40
+ if (block.listItem) return `${" ".repeat(Math.max(0, (block.level ?? 1) - 1))}${block.listItem === "number" ? "1." : "-"} ${text}`;
41
+ if (block.style && block.style.startsWith("h")) {
42
+ const level = parseInt(block.style.substring(1), 10);
43
+ if (level >= 1 && level <= 6) return `${"#".repeat(level)} ${text}`;
44
+ }
45
+ if (block.style === "blockquote") return `> ${text}`;
46
+ return text;
47
+ }
48
+ function renderSpans(spans, markDefs) {
49
+ let result = "";
50
+ for (const span of spans) {
51
+ if (span._type !== "span") continue;
52
+ let text = span.text ?? "";
53
+ const marks = span.marks ?? [];
54
+ for (const mark of marks) {
55
+ const def = markDefs.find((d) => d._key === mark);
56
+ if (def) {
57
+ if (def._type === "link") text = `[${text}](${def.href ?? ""})`;
58
+ } else switch (mark) {
59
+ case "strong":
60
+ text = `**${text}**`;
61
+ break;
62
+ case "em":
63
+ text = `_${text}_`;
64
+ break;
65
+ case "code":
66
+ text = `\`${text}\``;
67
+ break;
68
+ case "strike-through":
69
+ case "strikethrough":
70
+ text = `~~${text}~~`;
71
+ break;
72
+ }
73
+ }
74
+ result += text;
75
+ }
76
+ return result;
77
+ }
78
+ const OPAQUE_FENCE_PATTERN = /^<!--ec:block (.+) -->$/;
79
+ const HEADING_PATTERN = /^(#{1,6})\s+(.+)$/;
80
+ const UNORDERED_LIST_PATTERN = /^(\s*)[-*+]\s+(.+)$/;
81
+ const ORDERED_LIST_PATTERN = /^(\s*)\d+\.\s+(.+)$/;
82
+ const IMAGE_PATTERN = /^!\[([^\]]*)\]\(([^)]+)\)$/;
83
+ const INLINE_MARKDOWN_PATTERN = /(\*\*(.+?)\*\*)|(_(.+?)_)|(`(.+?)`)|(\[(.+?)\]\((.+?)\))|(~~(.+?)~~)/g;
84
+ /**
85
+ * Convert Markdown to Portable Text blocks.
86
+ * Opaque fences (<!--ec:block ... -->) are deserialized and spliced back in.
87
+ */
88
+ function markdownToPortableText(markdown) {
89
+ const blocks = [];
90
+ const lines = markdown.split("\n");
91
+ let i = 0;
92
+ while (i < lines.length) {
93
+ const line = lines[i];
94
+ const opaqueMatch = line.match(OPAQUE_FENCE_PATTERN);
95
+ if (opaqueMatch) {
96
+ try {
97
+ blocks.push(JSON.parse(opaqueMatch[1]));
98
+ } catch {
99
+ blocks.push(makeBlock(line));
100
+ }
101
+ i++;
102
+ continue;
103
+ }
104
+ if (line.startsWith("```")) {
105
+ const lang = line.slice(3).trim();
106
+ const codeLines = [];
107
+ i++;
108
+ while (i < lines.length && !lines[i].startsWith("```")) {
109
+ codeLines.push(lines[i]);
110
+ i++;
111
+ }
112
+ blocks.push({
113
+ _type: "code",
114
+ _key: generateKey(),
115
+ language: lang || void 0,
116
+ code: codeLines.join("\n")
117
+ });
118
+ i++;
119
+ continue;
120
+ }
121
+ if (line.trim() === "") {
122
+ i++;
123
+ continue;
124
+ }
125
+ const headingMatch = line.match(HEADING_PATTERN);
126
+ if (headingMatch) {
127
+ blocks.push(makeBlock(headingMatch[2], `h${headingMatch[1].length}`));
128
+ i++;
129
+ continue;
130
+ }
131
+ if (line.startsWith("> ")) {
132
+ blocks.push(makeBlock(line.slice(2), "blockquote"));
133
+ i++;
134
+ continue;
135
+ }
136
+ const ulMatch = line.match(UNORDERED_LIST_PATTERN);
137
+ if (ulMatch) {
138
+ const level = Math.floor(ulMatch[1].length / 2) + 1;
139
+ blocks.push(makeListBlock(ulMatch[2], "bullet", level));
140
+ i++;
141
+ continue;
142
+ }
143
+ const olMatch = line.match(ORDERED_LIST_PATTERN);
144
+ if (olMatch) {
145
+ const level = Math.floor(olMatch[1].length / 2) + 1;
146
+ blocks.push(makeListBlock(olMatch[2], "number", level));
147
+ i++;
148
+ continue;
149
+ }
150
+ const imgMatch = line.match(IMAGE_PATTERN);
151
+ if (imgMatch) {
152
+ blocks.push({
153
+ _type: "image",
154
+ _key: generateKey(),
155
+ alt: imgMatch[1],
156
+ asset: { url: imgMatch[2] }
157
+ });
158
+ i++;
159
+ continue;
160
+ }
161
+ blocks.push(makeBlock(line));
162
+ i++;
163
+ }
164
+ return blocks;
165
+ }
166
+ function makeBlock(text, style = "normal") {
167
+ const { spans, markDefs } = parseInline(text);
168
+ return {
169
+ _type: "block",
170
+ _key: generateKey(),
171
+ style,
172
+ markDefs,
173
+ children: spans
174
+ };
175
+ }
176
+ function makeListBlock(text, listItem, level) {
177
+ const { spans, markDefs } = parseInline(text);
178
+ return {
179
+ _type: "block",
180
+ _key: generateKey(),
181
+ style: "normal",
182
+ listItem,
183
+ level,
184
+ markDefs,
185
+ children: spans
186
+ };
187
+ }
188
+ /**
189
+ * Parse inline markdown (bold, italic, code, links, strikethrough) into PT spans + markDefs.
190
+ */
191
+ function parseInline(text) {
192
+ const spans = [];
193
+ const markDefs = [];
194
+ const regex = INLINE_MARKDOWN_PATTERN;
195
+ let lastIndex = 0;
196
+ let match;
197
+ while ((match = regex.exec(text)) !== null) {
198
+ if (match.index > lastIndex) spans.push({
199
+ _type: "span",
200
+ _key: generateKey(),
201
+ text: text.slice(lastIndex, match.index),
202
+ marks: []
203
+ });
204
+ if (match[2] != null) spans.push({
205
+ _type: "span",
206
+ _key: generateKey(),
207
+ text: match[2],
208
+ marks: ["strong"]
209
+ });
210
+ else if (match[4] != null) spans.push({
211
+ _type: "span",
212
+ _key: generateKey(),
213
+ text: match[4],
214
+ marks: ["em"]
215
+ });
216
+ else if (match[6] != null) spans.push({
217
+ _type: "span",
218
+ _key: generateKey(),
219
+ text: match[6],
220
+ marks: ["code"]
221
+ });
222
+ else if (match[8] != null && match[9] != null) {
223
+ const key = generateKey();
224
+ markDefs.push({
225
+ _key: key,
226
+ _type: "link",
227
+ href: match[9]
228
+ });
229
+ spans.push({
230
+ _type: "span",
231
+ _key: generateKey(),
232
+ text: match[8],
233
+ marks: [key]
234
+ });
235
+ } else if (match[11] != null) spans.push({
236
+ _type: "span",
237
+ _key: generateKey(),
238
+ text: match[11],
239
+ marks: ["strike-through"]
240
+ });
241
+ lastIndex = match.index + match[0].length;
242
+ }
243
+ if (lastIndex < text.length) spans.push({
244
+ _type: "span",
245
+ _key: generateKey(),
246
+ text: text.slice(lastIndex),
247
+ marks: []
248
+ });
249
+ if (spans.length === 0) spans.push({
250
+ _type: "span",
251
+ _key: generateKey(),
252
+ text,
253
+ marks: []
254
+ });
255
+ return {
256
+ spans,
257
+ markDefs
258
+ };
259
+ }
260
+ let keyCounter = 0;
261
+ function generateKey() {
262
+ return `k${(keyCounter++).toString(36)}`;
263
+ }
264
+ /**
265
+ * Convert content data for reading: PT fields -> markdown strings.
266
+ * Only converts fields with type "portableText" that contain arrays.
267
+ */
268
+ function convertDataForRead(data, fields, raw = false) {
269
+ if (raw) return data;
270
+ const result = { ...data };
271
+ for (const field of fields) if (field.type === "portableText" && Array.isArray(result[field.slug])) result[field.slug] = portableTextToMarkdown(result[field.slug]);
272
+ return result;
273
+ }
274
+ /**
275
+ * Convert content data for writing: markdown strings -> PT arrays.
276
+ * Only converts fields with type "portableText" that contain strings.
277
+ */
278
+ function convertDataForWrite(data, fields) {
279
+ const result = { ...data };
280
+ for (const field of fields) if (field.type === "portableText" && typeof result[field.slug] === "string") result[field.slug] = markdownToPortableText(result[field.slug]);
281
+ return result;
282
+ }
283
+
284
+ //#endregion
285
+ //#region src/client/transport.ts
286
+ /**
287
+ * Transport layer for the Dineway client.
288
+ *
289
+ * Implements a composable interceptor pipeline that modifies requests
290
+ * and responses. The client calls `transport.fetch(request)` — everything
291
+ * else (auth, CSRF, retry) is handled by interceptors.
292
+ */
293
+ const COOKIE_NAME_VALUE_PATTERN = /^([^;]+)/;
294
+ function baseFetch(request) {
295
+ return globalThis.fetch(request);
296
+ }
297
+ /**
298
+ * Creates a fetch function that runs requests through an interceptor pipeline.
299
+ */
300
+ function createTransport(options = {}) {
301
+ const interceptors = options.interceptors ?? [];
302
+ let chain = baseFetch;
303
+ for (let i = interceptors.length - 1; i >= 0; i--) {
304
+ const interceptor = interceptors[i];
305
+ const next = chain;
306
+ chain = (req) => interceptor(req, next);
307
+ }
308
+ return { fetch: chain };
309
+ }
310
+ /**
311
+ * Adds X-Dineway-Request: 1 and Origin headers to mutation requests
312
+ * (POST, PUT, DELETE). The custom header satisfies Dineway's CSRF check;
313
+ * the Origin header satisfies Astro's built-in origin verification which
314
+ * rejects server-side POST requests that lack a matching Origin.
315
+ */
316
+ function csrfInterceptor() {
317
+ const MUTATION_METHODS = new Set([
318
+ "POST",
319
+ "PUT",
320
+ "DELETE",
321
+ "PATCH"
322
+ ]);
323
+ return (request, next) => {
324
+ if (MUTATION_METHODS.has(request.method)) {
325
+ const headers = new Headers(request.headers);
326
+ headers.set("X-Dineway-Request", "1");
327
+ if (!headers.has("Origin")) {
328
+ const url = new URL(request.url);
329
+ headers.set("Origin", url.origin);
330
+ }
331
+ return next(new Request(request, { headers }));
332
+ }
333
+ return next(request);
334
+ };
335
+ }
336
+ /**
337
+ * Adds Authorization: Bearer header from a static token.
338
+ */
339
+ function tokenInterceptor(token) {
340
+ return (request, next) => {
341
+ const headers = new Headers(request.headers);
342
+ headers.set("Authorization", `Bearer ${token}`);
343
+ return next(new Request(request, { headers }));
344
+ };
345
+ }
346
+ /**
347
+ * Dev bypass interceptor. Calls the dev-bypass endpoint on first request
348
+ * to establish a session, then forwards the session cookie on subsequent
349
+ * requests.
350
+ */
351
+ function devBypassInterceptor(baseUrl) {
352
+ let sessionCookie = null;
353
+ let initializing = null;
354
+ async function init() {
355
+ const bypassUrl = new URL("/_dineway/api/auth/dev-bypass", baseUrl);
356
+ const res = await globalThis.fetch(bypassUrl, { redirect: "manual" });
357
+ const setCookie = res.headers.get("set-cookie");
358
+ if (setCookie) {
359
+ const match = setCookie.match(COOKIE_NAME_VALUE_PATTERN);
360
+ if (match) sessionCookie = match[1];
361
+ }
362
+ if (res.body) await res.text().catch(() => {});
363
+ }
364
+ return async (request, next) => {
365
+ if (!sessionCookie) {
366
+ if (!initializing) initializing = init();
367
+ await initializing;
368
+ }
369
+ if (sessionCookie) {
370
+ const headers = new Headers(request.headers);
371
+ const existing = headers.get("cookie");
372
+ headers.set("cookie", existing ? `${existing}; ${sessionCookie}` : sessionCookie);
373
+ return next(new Request(request, { headers }));
374
+ }
375
+ return next(request);
376
+ };
377
+ }
378
+ /**
379
+ * Auto-refreshes expired OAuth tokens on 401 responses.
380
+ * Requires a refresh token and the token endpoint URL.
381
+ */
382
+ function refreshInterceptor(options) {
383
+ let refreshing = null;
384
+ async function refresh() {
385
+ const res = await globalThis.fetch(options.tokenEndpoint, {
386
+ method: "POST",
387
+ headers: { "Content-Type": "application/json" },
388
+ body: JSON.stringify({
389
+ grant_type: "refresh_token",
390
+ refresh_token: options.refreshToken
391
+ })
392
+ });
393
+ if (!res.ok) return null;
394
+ const data = await res.json();
395
+ const expiresAt = data.expires_in ? new Date(Date.now() + data.expires_in * 1e3).toISOString() : new Date(Date.now() + 36e5).toISOString();
396
+ if (options.onTokenRefreshed) options.onTokenRefreshed(data.access_token, data.refresh_token ?? options.refreshToken, expiresAt);
397
+ return data.access_token;
398
+ }
399
+ return async (request, next) => {
400
+ const response = await next(request);
401
+ if (response.status === 401) {
402
+ if (!refreshing) refreshing = refresh().finally(() => {
403
+ refreshing = null;
404
+ });
405
+ const newToken = await refreshing;
406
+ if (newToken) {
407
+ const headers = new Headers(request.headers);
408
+ headers.set("Authorization", `Bearer ${newToken}`);
409
+ return next(new Request(request, { headers }));
410
+ }
411
+ }
412
+ return response;
413
+ };
414
+ }
415
+
416
+ //#endregion
417
+ export { tokenInterceptor as a, markdownToPortableText as c, refreshInterceptor as i, portableTextToMarkdown as l, csrfInterceptor as n, convertDataForRead as o, devBypassInterceptor as r, convertDataForWrite as s, createTransport as t };
@@ -0,0 +1,41 @@
1
+ //#region src/client/transport.d.ts
2
+ /**
3
+ * Transport layer for the Dineway client.
4
+ *
5
+ * Implements a composable interceptor pipeline that modifies requests
6
+ * and responses. The client calls `transport.fetch(request)` — everything
7
+ * else (auth, CSRF, retry) is handled by interceptors.
8
+ */
9
+ /**
10
+ * An interceptor can modify the request, call next(), inspect
11
+ * the response, and optionally retry.
12
+ */
13
+ type Interceptor = (request: Request, next: (request: Request) => Promise<Response>) => Promise<Response>;
14
+ interface TransportOptions {
15
+ interceptors?: Interceptor[];
16
+ }
17
+ /**
18
+ * Creates a fetch function that runs requests through an interceptor pipeline.
19
+ */
20
+ declare function createTransport(options?: TransportOptions): {
21
+ fetch: (request: Request) => Promise<Response>;
22
+ };
23
+ /**
24
+ * Adds X-Dineway-Request: 1 and Origin headers to mutation requests
25
+ * (POST, PUT, DELETE). The custom header satisfies Dineway's CSRF check;
26
+ * the Origin header satisfies Astro's built-in origin verification which
27
+ * rejects server-side POST requests that lack a matching Origin.
28
+ */
29
+ declare function csrfInterceptor(): Interceptor;
30
+ /**
31
+ * Adds Authorization: Bearer header from a static token.
32
+ */
33
+ declare function tokenInterceptor(token: string): Interceptor;
34
+ /**
35
+ * Dev bypass interceptor. Calls the dev-bypass endpoint on first request
36
+ * to establish a session, then forwards the session cookie on subsequent
37
+ * requests.
38
+ */
39
+ declare function devBypassInterceptor(baseUrl: string): Interceptor;
40
+ //#endregion
41
+ export { tokenInterceptor as a, devBypassInterceptor as i, createTransport as n, csrfInterceptor as r, Interceptor as t };
@@ -0,0 +1,30 @@
1
+ import { r as encodeBase64, t as decodeBase64 } from "./base64-F8-DUraK.mjs";
2
+
3
+ //#region src/database/repositories/types.ts
4
+ /** Encode a cursor from order value + id */
5
+ function encodeCursor(orderValue, id) {
6
+ return encodeBase64(JSON.stringify({
7
+ orderValue,
8
+ id
9
+ }));
10
+ }
11
+ /** Decode a cursor to order value + id. Returns null if invalid. */
12
+ function decodeCursor(cursor) {
13
+ try {
14
+ const parsed = JSON.parse(decodeBase64(cursor));
15
+ if (typeof parsed.orderValue === "string" && typeof parsed.id === "string") return parsed;
16
+ return null;
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+ var DinewayValidationError = class extends Error {
22
+ constructor(message, details) {
23
+ super(message);
24
+ this.details = details;
25
+ this.name = "DinewayValidationError";
26
+ }
27
+ };
28
+
29
+ //#endregion
30
+ export { decodeCursor as n, encodeCursor as r, DinewayValidationError as t };