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,267 @@
1
+ import { encode } from "blurhash";
2
+ import { imageSize } from "image-size";
3
+
4
+ //#region src/media/normalize.ts
5
+ const INTERNAL_MEDIA_PREFIX = "/_dineway/api/media/file/";
6
+ const URL_PATTERN = /^https?:\/\//;
7
+ /**
8
+ * Normalize a media field value into a consistent MediaValue shape.
9
+ *
10
+ * - `null`/`undefined` → `null`
11
+ * - Bare URL string → `{ provider: "external", id: "", src: url }`
12
+ * - Bare internal media URL → resolved via local provider's `get()`
13
+ * - Object with `provider` + `id` → enriched with missing fields from provider
14
+ */
15
+ async function normalizeMediaValue(value, getProvider) {
16
+ if (value == null) return null;
17
+ if (typeof value === "string") return normalizeStringUrl(value, getProvider);
18
+ if (!isRecord(value)) return null;
19
+ if (!("id" in value) && !("src" in value)) return null;
20
+ const provider = (typeof value.provider === "string" ? value.provider : void 0) || "local";
21
+ const id = typeof value.id === "string" ? value.id : "";
22
+ if (provider === "external") return recordToMediaValue(value);
23
+ const result = {
24
+ ...recordToMediaValue(value),
25
+ provider
26
+ };
27
+ if (provider === "local") delete result.src;
28
+ const needsDimensions = result.width == null || result.height == null;
29
+ const needsStorageKey = provider === "local" && !result.meta?.storageKey;
30
+ const needsFileInfo = !result.mimeType || !result.filename;
31
+ if (!(needsDimensions || needsStorageKey || needsFileInfo) || !id) return result;
32
+ const mediaProvider = getProvider(provider);
33
+ if (!mediaProvider?.get) return result;
34
+ let providerItem;
35
+ try {
36
+ providerItem = await mediaProvider.get(id);
37
+ } catch {
38
+ return result;
39
+ }
40
+ if (!providerItem) return result;
41
+ return mergeProviderData(result, providerItem);
42
+ }
43
+ function normalizeStringUrl(url, getProvider) {
44
+ if (url.startsWith(INTERNAL_MEDIA_PREFIX)) return resolveInternalUrl(url, getProvider);
45
+ if (URL_PATTERN.test(url)) return Promise.resolve({
46
+ provider: "external",
47
+ id: "",
48
+ src: url
49
+ });
50
+ return Promise.resolve({
51
+ provider: "external",
52
+ id: "",
53
+ src: url
54
+ });
55
+ }
56
+ async function resolveInternalUrl(url, getProvider) {
57
+ const storageKey = url.slice(25);
58
+ const localProvider = getProvider("local");
59
+ if (!localProvider?.get) return {
60
+ provider: "external",
61
+ id: "",
62
+ src: url
63
+ };
64
+ let item;
65
+ try {
66
+ item = await localProvider.get(storageKey);
67
+ } catch {
68
+ return {
69
+ provider: "external",
70
+ id: "",
71
+ src: url
72
+ };
73
+ }
74
+ if (!item) return {
75
+ provider: "external",
76
+ id: "",
77
+ src: url
78
+ };
79
+ return {
80
+ provider: "local",
81
+ id: item.id,
82
+ filename: item.filename,
83
+ mimeType: item.mimeType,
84
+ width: item.width,
85
+ height: item.height,
86
+ alt: item.alt,
87
+ meta: item.meta
88
+ };
89
+ }
90
+ /**
91
+ * Merge provider data into an existing MediaValue, preserving caller-supplied fields.
92
+ * Caller `alt` takes priority over provider `alt` (per-usage, not per-image).
93
+ */
94
+ function mergeProviderData(existing, item) {
95
+ const result = { ...existing };
96
+ if (result.width == null && item.width != null) result.width = item.width;
97
+ if (result.height == null && item.height != null) result.height = item.height;
98
+ if (!result.filename && item.filename) result.filename = item.filename;
99
+ if (!result.mimeType && item.mimeType) result.mimeType = item.mimeType;
100
+ if (!result.alt && item.alt) result.alt = item.alt;
101
+ if (item.meta) result.meta = {
102
+ ...item.meta,
103
+ ...result.meta
104
+ };
105
+ return result;
106
+ }
107
+ function isRecord(value) {
108
+ return typeof value === "object" && value !== null && !Array.isArray(value);
109
+ }
110
+ /**
111
+ * Extract known MediaValue fields from a runtime-checked record.
112
+ * Avoids unsafe `as MediaValue` cast by reading each property explicitly.
113
+ */
114
+ function recordToMediaValue(obj) {
115
+ const result = { id: typeof obj.id === "string" ? obj.id : "" };
116
+ if (typeof obj.provider === "string") result.provider = obj.provider;
117
+ if (typeof obj.src === "string") result.src = obj.src;
118
+ if (typeof obj.previewUrl === "string") result.previewUrl = obj.previewUrl;
119
+ if (typeof obj.filename === "string") result.filename = obj.filename;
120
+ if (typeof obj.mimeType === "string") result.mimeType = obj.mimeType;
121
+ if (typeof obj.width === "number") result.width = obj.width;
122
+ if (typeof obj.height === "number") result.height = obj.height;
123
+ if (typeof obj.alt === "string") result.alt = obj.alt;
124
+ if (isRecord(obj.meta)) result.meta = obj.meta;
125
+ return result;
126
+ }
127
+
128
+ //#endregion
129
+ //#region src/media/placeholder.ts
130
+ /**
131
+ * Image Placeholder Generation
132
+ *
133
+ * Generates blurhash and dominant color from image buffers for LQIP support.
134
+ * Decodes images via jpeg-js (pure JS) and upng-js (pure JS, uses pako for
135
+ * deflate). No Node-specific dependencies — works in Workers and Node SSR.
136
+ */
137
+ const SUPPORTED_TYPES = {
138
+ "image/jpeg": "jpeg",
139
+ "image/jpg": "jpeg",
140
+ "image/png": "png"
141
+ };
142
+ /** Max width for blurhash input. Encode is O(w*h*components), so downsample first. */
143
+ const MAX_ENCODE_WIDTH = 32;
144
+ /** Max decoded RGBA size (32 MB). Images exceeding this skip placeholder generation. */
145
+ const MAX_DECODED_BYTES = 32 * 1024 * 1024;
146
+ /**
147
+ * Decode a JPEG buffer into raw RGBA pixel data.
148
+ */
149
+ async function decodeJpeg(buffer) {
150
+ const { decode } = await import("jpeg-js");
151
+ const result = decode(buffer, { useTArray: true });
152
+ return {
153
+ width: result.width,
154
+ height: result.height,
155
+ data: result.data
156
+ };
157
+ }
158
+ /**
159
+ * Decode a PNG buffer into raw RGBA pixel data.
160
+ * Uses upng-js (pure JS with pako deflate) — no Node zlib dependency.
161
+ */
162
+ async function decodePng(buffer) {
163
+ const UPNG = (await import("upng-js")).default;
164
+ const img = UPNG.decode(buffer.buffer);
165
+ const frames = UPNG.toRGBA8(img);
166
+ const rgba = new Uint8Array(frames[0]);
167
+ return {
168
+ width: img.width,
169
+ height: img.height,
170
+ data: rgba
171
+ };
172
+ }
173
+ /**
174
+ * Extract the dominant color from RGBA pixel data.
175
+ * Simple average of all non-transparent pixels.
176
+ */
177
+ function extractDominantColor(data, width, height) {
178
+ let r = 0;
179
+ let g = 0;
180
+ let b = 0;
181
+ let count = 0;
182
+ const len = width * height * 4;
183
+ for (let i = 0; i < len; i += 4) {
184
+ if (data[i + 3] < 128) continue;
185
+ r += data[i];
186
+ g += data[i + 1];
187
+ b += data[i + 2];
188
+ count++;
189
+ }
190
+ if (count === 0) return "rgb(0,0,0)";
191
+ return `rgb(${Math.round(r / count)},${Math.round(g / count)},${Math.round(b / count)})`;
192
+ }
193
+ /**
194
+ * Read image dimensions from headers without decoding pixel data.
195
+ */
196
+ function getImageDimensions(buffer) {
197
+ try {
198
+ const result = imageSize(buffer);
199
+ if (result.width != null && result.height != null) return {
200
+ width: result.width,
201
+ height: result.height
202
+ };
203
+ return null;
204
+ } catch {
205
+ return null;
206
+ }
207
+ }
208
+ /**
209
+ * Generate blurhash and dominant color from an image buffer.
210
+ * Returns null for non-image MIME types or on failure.
211
+ *
212
+ * @param dimensions - Optional pre-known dimensions. Used as a fallback when
213
+ * image-size cannot parse the buffer (e.g. truncated headers). When the
214
+ * decoded size (width * height * 4) exceeds MAX_DECODED_BYTES, placeholder
215
+ * generation is skipped to avoid OOM on memory-constrained runtimes.
216
+ */
217
+ async function generatePlaceholder(buffer, mimeType, dimensions) {
218
+ const format = SUPPORTED_TYPES[mimeType];
219
+ if (!format) return null;
220
+ try {
221
+ const dims = getImageDimensions(buffer) ?? dimensions;
222
+ if (dims && dims.width * dims.height * 4 > MAX_DECODED_BYTES) return null;
223
+ const { width, height, data } = format === "jpeg" ? await decodeJpeg(buffer) : await decodePng(buffer);
224
+ if (width === 0 || height === 0) return null;
225
+ let encodePixels;
226
+ let encodeWidth;
227
+ let encodeHeight;
228
+ if (width > MAX_ENCODE_WIDTH) {
229
+ const scale = MAX_ENCODE_WIDTH / width;
230
+ encodeWidth = MAX_ENCODE_WIDTH;
231
+ encodeHeight = Math.max(1, Math.round(height * scale));
232
+ encodePixels = downsample(data, width, height, encodeWidth, encodeHeight);
233
+ } else {
234
+ encodeWidth = width;
235
+ encodeHeight = height;
236
+ encodePixels = new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength);
237
+ }
238
+ return {
239
+ blurhash: encode(encodePixels, encodeWidth, encodeHeight, 4, 3),
240
+ dominantColor: extractDominantColor(data, width, height)
241
+ };
242
+ } catch {
243
+ return null;
244
+ }
245
+ }
246
+ /**
247
+ * Nearest-neighbor downsample of RGBA pixel data.
248
+ */
249
+ function downsample(src, srcW, srcH, dstW, dstH) {
250
+ const dst = new Uint8ClampedArray(dstW * dstH * 4);
251
+ for (let y = 0; y < dstH; y++) {
252
+ const srcY = Math.floor(y * srcH / dstH);
253
+ for (let x = 0; x < dstW; x++) {
254
+ const srcX = Math.floor(x * srcW / dstW);
255
+ const srcIdx = (srcY * srcW + srcX) * 4;
256
+ const dstIdx = (y * dstW + x) * 4;
257
+ dst[dstIdx] = src[srcIdx];
258
+ dst[dstIdx + 1] = src[srcIdx + 1];
259
+ dst[dstIdx + 2] = src[srcIdx + 2];
260
+ dst[dstIdx + 3] = src[srcIdx + 3];
261
+ }
262
+ }
263
+ return dst;
264
+ }
265
+
266
+ //#endregion
267
+ export { normalizeMediaValue as n, generatePlaceholder as t };
@@ -0,0 +1,283 @@
1
+ //#region src/media/types.d.ts
2
+ /**
3
+ * Media Provider Types
4
+ *
5
+ * Media providers are pluggable sources for browsing, uploading, and embedding media.
6
+ * They enable integration with external services (Unsplash, Cloudinary, Mux, etc.)
7
+ * alongside the built-in local media library.
8
+ */
9
+ /**
10
+ * Serializable media provider configuration descriptor
11
+ * Returned by provider config functions (e.g., unsplash(), mux())
12
+ */
13
+ interface MediaProviderDescriptor<TConfig = Record<string, unknown>> {
14
+ /** Unique identifier, used in MediaValue.provider */
15
+ id: string;
16
+ /** Display name for admin UI */
17
+ name: string;
18
+ /** Icon for tab UI (emoji or URL) */
19
+ icon?: string;
20
+ /** Module path exporting createMediaProvider function */
21
+ entrypoint: string;
22
+ /** Optional React component module for custom admin UI */
23
+ adminModule?: string;
24
+ /** Capability flags determine UI behavior */
25
+ capabilities: MediaProviderCapabilities;
26
+ /** Serializable config passed to createMediaProvider at runtime */
27
+ config: TConfig;
28
+ }
29
+ /**
30
+ * Provider capabilities determine what UI elements to show
31
+ */
32
+ interface MediaProviderCapabilities {
33
+ /** Can list/browse media */
34
+ browse: boolean;
35
+ /** Supports text search */
36
+ search: boolean;
37
+ /** Can upload new media */
38
+ upload: boolean;
39
+ /** Can delete media */
40
+ delete: boolean;
41
+ }
42
+ /**
43
+ * Options for listing media
44
+ */
45
+ interface MediaListOptions {
46
+ /** Pagination cursor */
47
+ cursor?: string;
48
+ /** Max items to return (default 20) */
49
+ limit?: number;
50
+ /** Search query (if capabilities.search is true) */
51
+ query?: string;
52
+ /** Filter by MIME type prefix, e.g., "image/", "video/" */
53
+ mimeType?: string;
54
+ }
55
+ /**
56
+ * Result from listing media
57
+ */
58
+ interface MediaListResult {
59
+ items: MediaProviderItem[];
60
+ nextCursor?: string;
61
+ }
62
+ /**
63
+ * A media item as returned by a provider
64
+ * This is the provider's view of the item, before it's selected
65
+ */
66
+ interface MediaProviderItem {
67
+ /** Provider-specific ID */
68
+ id: string;
69
+ /** Original filename */
70
+ filename: string;
71
+ /** MIME type */
72
+ mimeType: string;
73
+ /** File size in bytes (if known) */
74
+ size?: number;
75
+ /** Dimensions (for images/video) */
76
+ width?: number;
77
+ height?: number;
78
+ /** Accessibility text */
79
+ alt?: string;
80
+ /** Preview URL for admin UI thumbnail */
81
+ previewUrl?: string;
82
+ /** Provider-specific metadata */
83
+ meta?: Record<string, unknown>;
84
+ }
85
+ /**
86
+ * Input for uploading media
87
+ */
88
+ interface MediaUploadInput {
89
+ file: File;
90
+ filename: string;
91
+ alt?: string;
92
+ }
93
+ /**
94
+ * Options for generating embed
95
+ */
96
+ interface EmbedOptions {
97
+ /** Desired width (provider may use for optimization) */
98
+ width?: number;
99
+ /** Desired height */
100
+ height?: number;
101
+ /** Image format preference */
102
+ format?: "webp" | "avif" | "jpeg" | "png" | "auto";
103
+ }
104
+ /**
105
+ * Embed result types
106
+ */
107
+ type EmbedResult = ImageEmbed | VideoEmbed | AudioEmbed | ComponentEmbed;
108
+ interface ImageEmbed {
109
+ type: "image";
110
+ src: string;
111
+ srcset?: string;
112
+ sizes?: string;
113
+ width?: number;
114
+ height?: number;
115
+ alt?: string;
116
+ /** Base URL without transforms, for responsive image generation */
117
+ cdnBaseUrl?: string;
118
+ /** For providers with URL-based transforms (Cloudinary, imgix) */
119
+ getSrc?: (opts: {
120
+ width?: number;
121
+ height?: number;
122
+ format?: string;
123
+ }) => string;
124
+ }
125
+ interface VideoEmbed {
126
+ type: "video";
127
+ /** Single source URL */
128
+ src?: string;
129
+ /** Multiple sources for format fallback */
130
+ sources?: Array<{
131
+ src: string;
132
+ type: string;
133
+ }>;
134
+ /** Poster/thumbnail image */
135
+ poster?: string;
136
+ width?: number;
137
+ height?: number;
138
+ /** Player controls */
139
+ controls?: boolean;
140
+ autoplay?: boolean;
141
+ muted?: boolean;
142
+ loop?: boolean;
143
+ playsinline?: boolean;
144
+ preload?: "none" | "metadata" | "auto";
145
+ crossorigin?: "anonymous" | "use-credentials";
146
+ }
147
+ interface AudioEmbed {
148
+ type: "audio";
149
+ src?: string;
150
+ sources?: Array<{
151
+ src: string;
152
+ type: string;
153
+ }>;
154
+ controls?: boolean;
155
+ autoplay?: boolean;
156
+ muted?: boolean;
157
+ loop?: boolean;
158
+ preload?: "none" | "metadata" | "auto";
159
+ }
160
+ interface ComponentEmbed {
161
+ type: "component";
162
+ /** Package to import from, e.g., "@mux/player-react" */
163
+ package: string;
164
+ /** Named export (default export if not specified) */
165
+ export?: string;
166
+ /** Props to pass to the component */
167
+ props: Record<string, unknown>;
168
+ }
169
+ /**
170
+ * Options for thumbnail generation
171
+ */
172
+ interface ThumbnailOptions {
173
+ /** Desired width */
174
+ width?: number;
175
+ /** Desired height */
176
+ height?: number;
177
+ }
178
+ /**
179
+ * Runtime media provider interface
180
+ * Implemented by provider entrypoints
181
+ */
182
+ interface MediaProvider {
183
+ /**
184
+ * List/search media items
185
+ */
186
+ list(options: MediaListOptions): Promise<MediaListResult>;
187
+ /**
188
+ * Get a single item by ID (optional, for refresh/validation)
189
+ */
190
+ get?(id: string): Promise<MediaProviderItem | null>;
191
+ /**
192
+ * Upload new media (if capabilities.upload is true)
193
+ */
194
+ upload?(input: MediaUploadInput): Promise<MediaProviderItem>;
195
+ /**
196
+ * Delete media (if capabilities.delete is true)
197
+ */
198
+ delete?(id: string): Promise<void>;
199
+ /**
200
+ * Get embed information for rendering this media item
201
+ * Called at runtime when rendering content
202
+ */
203
+ getEmbed(value: MediaValue, options?: EmbedOptions): Promise<EmbedResult> | EmbedResult;
204
+ /**
205
+ * Get a thumbnail URL for admin display
206
+ * For images: returns a resized image URL
207
+ * For videos: returns a poster/thumbnail URL
208
+ */
209
+ getThumbnailUrl?(id: string, mimeType?: string, options?: ThumbnailOptions): string;
210
+ }
211
+ /**
212
+ * Function signature for provider entrypoint modules
213
+ */
214
+ type CreateMediaProviderFn<TConfig = Record<string, unknown>> = (config: TConfig) => MediaProvider;
215
+ /**
216
+ * Media value stored in content fields
217
+ * This is what gets persisted when media is selected
218
+ *
219
+ * For backwards compatibility:
220
+ * - `provider` defaults to "local" if not specified
221
+ * - `src` is supported for legacy data or external URLs
222
+ */
223
+ interface MediaValue {
224
+ /** Provider ID, e.g., "local", "unsplash", "mux" (defaults to "local") */
225
+ provider?: string;
226
+ /** Provider-specific item ID */
227
+ id: string;
228
+ /** Direct URL (for local media or legacy data) */
229
+ src?: string;
230
+ /** Preview URL for admin display (external providers) */
231
+ previewUrl?: string;
232
+ /** Cached metadata for display without runtime lookup */
233
+ filename?: string;
234
+ mimeType?: string;
235
+ width?: number;
236
+ height?: number;
237
+ alt?: string;
238
+ /** Provider-specific data needed for embedding */
239
+ meta?: Record<string, unknown>;
240
+ }
241
+ /**
242
+ * Convert a MediaProviderItem to a MediaValue for storage
243
+ */
244
+ declare function mediaItemToValue(providerId: string, item: MediaProviderItem): MediaValue;
245
+ //#endregion
246
+ //#region src/media/normalize.d.ts
247
+ /**
248
+ * Normalize a media field value into a consistent MediaValue shape.
249
+ *
250
+ * - `null`/`undefined` → `null`
251
+ * - Bare URL string → `{ provider: "external", id: "", src: url }`
252
+ * - Bare internal media URL → resolved via local provider's `get()`
253
+ * - Object with `provider` + `id` → enriched with missing fields from provider
254
+ */
255
+ declare function normalizeMediaValue(value: unknown, getProvider: (id: string) => MediaProvider | undefined): Promise<MediaValue | null>;
256
+ //#endregion
257
+ //#region src/media/placeholder.d.ts
258
+ /**
259
+ * Image Placeholder Generation
260
+ *
261
+ * Generates blurhash and dominant color from image buffers for LQIP support.
262
+ * Decodes images via jpeg-js (pure JS) and upng-js (pure JS, uses pako for
263
+ * deflate). No Node-specific dependencies — works in Workers and Node SSR.
264
+ */
265
+ interface PlaceholderData {
266
+ blurhash: string;
267
+ dominantColor: string;
268
+ }
269
+ /**
270
+ * Generate blurhash and dominant color from an image buffer.
271
+ * Returns null for non-image MIME types or on failure.
272
+ *
273
+ * @param dimensions - Optional pre-known dimensions. Used as a fallback when
274
+ * image-size cannot parse the buffer (e.g. truncated headers). When the
275
+ * decoded size (width * height * 4) exceeds MAX_DECODED_BYTES, placeholder
276
+ * generation is skipped to avoid OOM on memory-constrained runtimes.
277
+ */
278
+ declare function generatePlaceholder(buffer: Uint8Array, mimeType: string, dimensions?: {
279
+ width: number;
280
+ height: number;
281
+ }): Promise<PlaceholderData | null>;
282
+ //#endregion
283
+ export { MediaValue as _, ComponentEmbed as a, mediaItemToValue as b, EmbedResult as c, MediaListResult as d, MediaProvider as f, MediaUploadInput as g, MediaProviderItem as h, AudioEmbed as i, ImageEmbed as l, MediaProviderDescriptor as m, generatePlaceholder as n, CreateMediaProviderFn as o, MediaProviderCapabilities as p, normalizeMediaValue as r, EmbedOptions as s, PlaceholderData as t, MediaListOptions as u, ThumbnailOptions as v, VideoEmbed as y };
@@ -0,0 +1,57 @@
1
+ //#region src/plugin-utils.d.ts
2
+ /**
3
+ * Shared utilities for plugin admin UIs.
4
+ *
5
+ * Plugin admin components (`admin.tsx`) run inside the Dineway admin dashboard.
6
+ * This module provides the common helpers they all need: API fetching with CSRF
7
+ * protection, response envelope unwrapping, and type narrowing.
8
+ *
9
+ * Import as: `import { apiFetch, parseApiResponse, isRecord } from "dineway/plugin-utils";`
10
+ */
11
+ /**
12
+ * Fetch wrapper that adds the `X-Dineway-Request` CSRF protection header.
13
+ *
14
+ * All plugin admin API calls should use this instead of raw `fetch()`.
15
+ * State-changing endpoints reject requests without this header.
16
+ */
17
+ declare function apiFetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
18
+ /**
19
+ * Parse an API response, unwrapping the `{ data: T }` envelope.
20
+ *
21
+ * All plugin API routes return success responses wrapped in `{ data: ... }`
22
+ * by `apiSuccess()`. This helper unwraps that envelope and handles errors.
23
+ *
24
+ * On error responses (non-2xx), throws an Error with the server's message
25
+ * (from `{ error: { message } }`) or the fallback message.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const res = await apiFetch("/_dineway/api/plugins/my-plugin/items");
30
+ * const { items } = await parseApiResponse<{ items: Item[] }>(res, "Failed to load items");
31
+ * ```
32
+ */
33
+ declare function parseApiResponse<T>(response: Response, fallbackMessage?: string): Promise<T>;
34
+ /**
35
+ * Extract the error message from a failed API response.
36
+ *
37
+ * Error responses use the shape `{ error: { code, message } }`. This helper
38
+ * parses that body and returns the message, falling back to the provided default.
39
+ * Swallows JSON parse failures gracefully.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * if (!res.ok) {
44
+ * setError(await getErrorMessage(res, "Failed to save"));
45
+ * return;
46
+ * }
47
+ * ```
48
+ */
49
+ declare function getErrorMessage(response: Response, fallback: string): Promise<string>;
50
+ /**
51
+ * Narrow `unknown` to a plain object record.
52
+ *
53
+ * Useful for safely inspecting untyped API responses before accessing properties.
54
+ */
55
+ declare function isRecord(value: unknown): value is Record<string, unknown>;
56
+ //#endregion
57
+ export { apiFetch, getErrorMessage, isRecord, parseApiResponse };
@@ -0,0 +1,77 @@
1
+ //#region src/plugin-utils.ts
2
+ /**
3
+ * Shared utilities for plugin admin UIs.
4
+ *
5
+ * Plugin admin components (`admin.tsx`) run inside the Dineway admin dashboard.
6
+ * This module provides the common helpers they all need: API fetching with CSRF
7
+ * protection, response envelope unwrapping, and type narrowing.
8
+ *
9
+ * Import as: `import { apiFetch, parseApiResponse, isRecord } from "dineway/plugin-utils";`
10
+ */
11
+ /**
12
+ * Fetch wrapper that adds the `X-Dineway-Request` CSRF protection header.
13
+ *
14
+ * All plugin admin API calls should use this instead of raw `fetch()`.
15
+ * State-changing endpoints reject requests without this header.
16
+ */
17
+ function apiFetch(input, init) {
18
+ const headers = new Headers(init?.headers);
19
+ headers.set("X-Dineway-Request", "1");
20
+ return fetch(input, {
21
+ ...init,
22
+ headers
23
+ });
24
+ }
25
+ /**
26
+ * Parse an API response, unwrapping the `{ data: T }` envelope.
27
+ *
28
+ * All plugin API routes return success responses wrapped in `{ data: ... }`
29
+ * by `apiSuccess()`. This helper unwraps that envelope and handles errors.
30
+ *
31
+ * On error responses (non-2xx), throws an Error with the server's message
32
+ * (from `{ error: { message } }`) or the fallback message.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const res = await apiFetch("/_dineway/api/plugins/my-plugin/items");
37
+ * const { items } = await parseApiResponse<{ items: Item[] }>(res, "Failed to load items");
38
+ * ```
39
+ */
40
+ async function parseApiResponse(response, fallbackMessage = "Request failed") {
41
+ if (!response.ok) throw new Error(await getErrorMessage(response, `${fallbackMessage}: ${response.statusText}`));
42
+ return (await response.json()).data;
43
+ }
44
+ /**
45
+ * Extract the error message from a failed API response.
46
+ *
47
+ * Error responses use the shape `{ error: { code, message } }`. This helper
48
+ * parses that body and returns the message, falling back to the provided default.
49
+ * Swallows JSON parse failures gracefully.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * if (!res.ok) {
54
+ * setError(await getErrorMessage(res, "Failed to save"));
55
+ * return;
56
+ * }
57
+ * ```
58
+ */
59
+ async function getErrorMessage(response, fallback) {
60
+ const body = await response.json().catch(() => ({}));
61
+ if (isRecord(body) && isRecord(body.error)) {
62
+ const msg = body.error.message;
63
+ if (typeof msg === "string") return msg;
64
+ }
65
+ return fallback;
66
+ }
67
+ /**
68
+ * Narrow `unknown` to a plain object record.
69
+ *
70
+ * Useful for safely inspecting untyped API responses before accessing properties.
71
+ */
72
+ function isRecord(value) {
73
+ return typeof value === "object" && value !== null && !Array.isArray(value);
74
+ }
75
+
76
+ //#endregion
77
+ export { apiFetch, getErrorMessage, isRecord, parseApiResponse };
@@ -0,0 +1,21 @@
1
+ import "../types-DkvMXalq.mjs";
2
+ import { wn as PluginDescriptor } from "../index-C-jx21qs.mjs";
3
+ import "../runner-B5l1JfOj.mjs";
4
+ import { $ as StandardPluginDefinition, J as ResolvedPlugin } from "../types-D38djUXv.mjs";
5
+ import "../validate-DVKJJ-M_.mjs";
6
+
7
+ //#region src/plugins/adapt-sandbox-entry.d.ts
8
+ /**
9
+ * Adapt a standard-format plugin definition into a ResolvedPlugin.
10
+ *
11
+ * This is the core of the unified plugin format. It takes the `{ hooks, routes }`
12
+ * export from a standard plugin and produces a ResolvedPlugin that can enter the
13
+ * HookPipeline alongside native plugins.
14
+ *
15
+ * @param definition - The standard plugin definition (from definePlugin() or raw export)
16
+ * @param descriptor - The plugin descriptor with id, version, capabilities, etc.
17
+ * @returns A ResolvedPlugin compatible with HookPipeline
18
+ */
19
+ declare function adaptSandboxEntry(definition: StandardPluginDefinition, descriptor: PluginDescriptor): ResolvedPlugin;
20
+ //#endregion
21
+ export { adaptSandboxEntry };