@aphexcms/cms-core 2.0.3 → 2.0.5
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/dist/cache/adapters/in-memory-cache-adapter.d.ts +21 -0
- package/dist/cache/adapters/in-memory-cache-adapter.d.ts.map +1 -0
- package/dist/cache/adapters/in-memory-cache-adapter.js +50 -0
- package/dist/cache/document-cache.d.ts +18 -0
- package/dist/cache/document-cache.d.ts.map +1 -0
- package/dist/cache/document-cache.js +45 -0
- package/dist/cache/index.d.ts +4 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +2 -0
- package/dist/cache/interfaces/cache.d.ts +18 -0
- package/dist/cache/interfaces/cache.d.ts.map +1 -0
- package/dist/cache/interfaces/cache.js +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/components/admin/SchemaField.svelte +14 -0
- package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/FileField.svelte +485 -0
- package/dist/components/admin/fields/FileField.svelte.d.ts +18 -0
- package/dist/components/admin/fields/FileField.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/ReferenceField.svelte +2 -2
- package/dist/components/fields/index.d.ts +1 -0
- package/dist/components/fields/index.d.ts.map +1 -1
- package/dist/components/fields/index.js +1 -0
- package/dist/graphql/schema.d.ts.map +1 -1
- package/dist/graphql/schema.js +15 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +11 -0
- package/dist/lib/cache/adapters/in-memory-cache-adapter.d.ts +21 -0
- package/dist/lib/cache/adapters/in-memory-cache-adapter.d.ts.map +1 -0
- package/dist/lib/cache/adapters/in-memory-cache-adapter.js +51 -0
- package/dist/lib/cache/adapters/in-memory-cache-adapter.js.map +1 -0
- package/dist/lib/cache/document-cache.d.ts +18 -0
- package/dist/lib/cache/document-cache.d.ts.map +1 -0
- package/dist/lib/cache/document-cache.js +46 -0
- package/dist/lib/cache/document-cache.js.map +1 -0
- package/dist/lib/cache/index.d.ts +4 -0
- package/dist/lib/cache/index.d.ts.map +1 -0
- package/dist/lib/cache/index.js +3 -0
- package/dist/lib/cache/index.js.map +1 -0
- package/dist/lib/cache/interfaces/cache.d.ts +18 -0
- package/dist/lib/cache/interfaces/cache.d.ts.map +1 -0
- package/dist/lib/cache/interfaces/cache.js +2 -0
- package/dist/lib/cache/interfaces/cache.js.map +1 -0
- package/dist/lib/client/index.d.ts +1 -0
- package/dist/lib/client/index.d.ts.map +1 -1
- package/dist/lib/client/index.js +1 -0
- package/dist/lib/client/index.js.map +1 -1
- package/dist/lib/components/fields/index.d.ts +1 -0
- package/dist/lib/components/fields/index.d.ts.map +1 -1
- package/dist/lib/components/fields/index.js +1 -0
- package/dist/lib/components/fields/index.js.map +1 -1
- package/dist/lib/graphql/schema.d.ts.map +1 -1
- package/dist/lib/graphql/schema.js +15 -1
- package/dist/lib/graphql/schema.js.map +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +11 -0
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/local-api/collection-api.d.ts +5 -1
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +63 -10
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/local-api/index.d.ts +13 -0
- package/dist/lib/local-api/index.d.ts.map +1 -1
- package/dist/lib/local-api/index.js +26 -2
- package/dist/lib/local-api/index.js.map +1 -1
- package/dist/lib/routes/assets.d.ts.map +1 -1
- package/dist/lib/routes/assets.js +14 -0
- package/dist/lib/routes/assets.js.map +1 -1
- package/dist/lib/routes/documents-by-id.js +18 -18
- package/dist/lib/routes/documents-by-id.js.map +1 -1
- package/dist/lib/routes/documents-publish.js +12 -12
- package/dist/lib/routes/documents-publish.js.map +1 -1
- package/dist/lib/schema-utils/validator.d.ts.map +1 -1
- package/dist/lib/schema-utils/validator.js +1 -0
- package/dist/lib/schema-utils/validator.js.map +1 -1
- package/dist/lib/server/index.d.ts +1 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +1 -0
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/services/hierarchy-service.d.ts +26 -0
- package/dist/lib/services/hierarchy-service.d.ts.map +1 -0
- package/dist/lib/services/hierarchy-service.js +64 -0
- package/dist/lib/services/hierarchy-service.js.map +1 -0
- package/dist/lib/services/index.d.ts +1 -0
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/index.js +1 -0
- package/dist/lib/services/index.js.map +1 -1
- package/dist/lib/storage/adapters/local-storage-adapter.d.ts.map +1 -1
- package/dist/lib/storage/adapters/local-storage-adapter.js +0 -14
- package/dist/lib/storage/adapters/local-storage-adapter.js.map +1 -1
- package/dist/lib/storage/interfaces/storage.d.ts +0 -1
- package/dist/lib/storage/interfaces/storage.d.ts.map +1 -1
- package/dist/lib/types/asset.d.ts +9 -0
- package/dist/lib/types/asset.d.ts.map +1 -1
- package/dist/lib/types/config.d.ts +7 -0
- package/dist/lib/types/config.d.ts.map +1 -1
- package/dist/lib/types/schemas.d.ts +10 -2
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/lib/utils/mime-detect.d.ts +22 -0
- package/dist/lib/utils/mime-detect.d.ts.map +1 -0
- package/dist/lib/utils/mime-detect.js +201 -0
- package/dist/lib/utils/mime-detect.js.map +1 -0
- package/dist/local-api/collection-api.d.ts +5 -1
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +63 -10
- package/dist/local-api/index.d.ts +13 -0
- package/dist/local-api/index.d.ts.map +1 -1
- package/dist/local-api/index.js +26 -2
- package/dist/routes/assets.d.ts.map +1 -1
- package/dist/routes/assets.js +14 -0
- package/dist/routes/documents-by-id.js +18 -18
- package/dist/routes/documents-publish.js +12 -12
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/services/hierarchy-service.d.ts +26 -0
- package/dist/services/hierarchy-service.d.ts.map +1 -0
- package/dist/services/hierarchy-service.js +63 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/storage/adapters/local-storage-adapter.d.ts.map +1 -1
- package/dist/storage/adapters/local-storage-adapter.js +0 -14
- package/dist/storage/interfaces/storage.d.ts +0 -1
- package/dist/storage/interfaces/storage.d.ts.map +1 -1
- package/dist/types/asset.d.ts +9 -0
- package/dist/types/asset.d.ts.map +1 -1
- package/dist/types/config.d.ts +7 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/schemas.d.ts +10 -2
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/utils/mime-detect.d.ts +22 -0
- package/dist/utils/mime-detect.d.ts.map +1 -0
- package/dist/utils/mime-detect.js +200 -0
- package/package.json +1 -1
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button } from '@aphexcms/ui/shadcn/button';
|
|
3
|
+
import { Upload, File as FileIcon, Download, Copy, CircleX } from '@lucide/svelte';
|
|
4
|
+
import type { FileValue } from '../../../types/asset';
|
|
5
|
+
import type { FileField as FileFieldType } from '../../../types/schemas';
|
|
6
|
+
import { assets } from '../../../api/assets';
|
|
7
|
+
import { toast } from 'svelte-sonner';
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuTrigger,
|
|
11
|
+
DropdownMenuContent,
|
|
12
|
+
DropdownMenuItem,
|
|
13
|
+
DropdownMenuSeparator,
|
|
14
|
+
DropdownMenuGroup
|
|
15
|
+
} from '@aphexcms/ui/shadcn/dropdown-menu';
|
|
16
|
+
import { Ellipsis } from '@lucide/svelte';
|
|
17
|
+
import elementEvents from '../../../utils/element-events';
|
|
18
|
+
import { copyUrlToClipboard, downloadFile } from '../../../utils/asset-actions';
|
|
19
|
+
import AssetBrowserModal from '../AssetBrowserModal.svelte';
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
field: FileFieldType;
|
|
23
|
+
value: FileValue | null;
|
|
24
|
+
validationClasses?: string;
|
|
25
|
+
onUpdate: (value: FileValue | null) => void;
|
|
26
|
+
schemaType?: string;
|
|
27
|
+
fieldPath?: string;
|
|
28
|
+
readonly?: boolean;
|
|
29
|
+
compact?: boolean;
|
|
30
|
+
arrayItem?: boolean;
|
|
31
|
+
organizationId?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let {
|
|
35
|
+
field,
|
|
36
|
+
value,
|
|
37
|
+
onUpdate,
|
|
38
|
+
validationClasses,
|
|
39
|
+
schemaType,
|
|
40
|
+
fieldPath,
|
|
41
|
+
readonly = false,
|
|
42
|
+
compact = false,
|
|
43
|
+
arrayItem = false,
|
|
44
|
+
organizationId
|
|
45
|
+
}: Props = $props();
|
|
46
|
+
|
|
47
|
+
let isDragging = $state(false);
|
|
48
|
+
let isUploading = $state(false);
|
|
49
|
+
let uploadError = $state<string | null>(null);
|
|
50
|
+
let fileInputRef: HTMLInputElement;
|
|
51
|
+
let showAssetBrowser = $state(false);
|
|
52
|
+
|
|
53
|
+
// Build accept string for file input from field.accept array
|
|
54
|
+
const acceptString = $derived(field.accept ? field.accept.join(',') : undefined);
|
|
55
|
+
|
|
56
|
+
async function uploadFile(file: File): Promise<FileValue | null> {
|
|
57
|
+
isUploading = true;
|
|
58
|
+
uploadError = null;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const formData = new FormData();
|
|
62
|
+
formData.append('file', file);
|
|
63
|
+
|
|
64
|
+
if (organizationId) formData.append('organizationId', organizationId);
|
|
65
|
+
if (schemaType) formData.append('schemaType', schemaType);
|
|
66
|
+
if (fieldPath) formData.append('fieldPath', fieldPath);
|
|
67
|
+
|
|
68
|
+
// Pass allowed MIME types for server-side validation
|
|
69
|
+
if (field.accept) {
|
|
70
|
+
formData.append('allowedMimeTypes', JSON.stringify(field.accept));
|
|
71
|
+
}
|
|
72
|
+
if (field.maxSize) {
|
|
73
|
+
formData.append('maxSize', String(field.maxSize));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result = await assets.upload(formData);
|
|
77
|
+
|
|
78
|
+
if (!result.success) {
|
|
79
|
+
throw new Error(result.error || 'Upload failed');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const asset = result.data;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
_type: 'file',
|
|
86
|
+
asset: {
|
|
87
|
+
_type: 'reference',
|
|
88
|
+
_ref: asset!.id
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
uploadError = error instanceof Error ? error.message : 'Upload failed';
|
|
93
|
+
return null;
|
|
94
|
+
} finally {
|
|
95
|
+
isUploading = false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function handleFileSelect(files: FileList | null) {
|
|
100
|
+
if (readonly || !files || files.length === 0) return;
|
|
101
|
+
|
|
102
|
+
const file = files[0]!;
|
|
103
|
+
|
|
104
|
+
// Client-side size check
|
|
105
|
+
if (field.maxSize && file.size > field.maxSize) {
|
|
106
|
+
const maxMB = (field.maxSize / (1024 * 1024)).toFixed(1);
|
|
107
|
+
uploadError = `File exceeds maximum size of ${maxMB} MB`;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const fileValue = await uploadFile(file);
|
|
112
|
+
if (fileValue) {
|
|
113
|
+
onUpdate(fileValue);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function handleDragOver(event: DragEvent) {
|
|
118
|
+
if (readonly) return;
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
isDragging = true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function handleDragLeave(event: DragEvent) {
|
|
124
|
+
if (readonly) return;
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
isDragging = false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function handleDrop(event: DragEvent) {
|
|
130
|
+
if (readonly) return;
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
isDragging = false;
|
|
133
|
+
handleFileSelect(event.dataTransfer?.files || null);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function handleFileInputChange(event: Event) {
|
|
137
|
+
if (readonly) return;
|
|
138
|
+
const target = event.target as HTMLInputElement;
|
|
139
|
+
handleFileSelect(target.files);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function openFileDialog() {
|
|
143
|
+
if (readonly) return;
|
|
144
|
+
fileInputRef?.click();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function removeFile() {
|
|
148
|
+
if (readonly) return;
|
|
149
|
+
onUpdate(null);
|
|
150
|
+
uploadError = null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Asset data state
|
|
154
|
+
let assetData = $state<any>(null);
|
|
155
|
+
let loadingAsset = $state(false);
|
|
156
|
+
let lastAssetId = $state<string | null>(null);
|
|
157
|
+
|
|
158
|
+
$effect(() => {
|
|
159
|
+
async function loadAsset() {
|
|
160
|
+
const assetId = value?.asset?._ref || null;
|
|
161
|
+
|
|
162
|
+
if (assetId !== lastAssetId) {
|
|
163
|
+
lastAssetId = assetId;
|
|
164
|
+
|
|
165
|
+
if (assetId) {
|
|
166
|
+
loadingAsset = true;
|
|
167
|
+
try {
|
|
168
|
+
const result = await assets.getById(assetId);
|
|
169
|
+
if (result.success) {
|
|
170
|
+
assetData = result.data;
|
|
171
|
+
} else {
|
|
172
|
+
toast.error('Failed to fetch asset details');
|
|
173
|
+
assetData = null;
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
toast.error('Failed to load file asset');
|
|
177
|
+
assetData = null;
|
|
178
|
+
} finally {
|
|
179
|
+
loadingAsset = false;
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
assetData = null;
|
|
183
|
+
loadingAsset = false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
loadAsset();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const fileUrl = $derived(assetData?.url || null);
|
|
191
|
+
|
|
192
|
+
const displayName = $derived(
|
|
193
|
+
assetData?.originalFilename || assetData?.filename || value?.asset?._ref || 'File'
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const fileSize = $derived(
|
|
197
|
+
assetData?.size
|
|
198
|
+
? assetData.size > 1024 * 1024
|
|
199
|
+
? `${(assetData.size / (1024 * 1024)).toFixed(1)} MB`
|
|
200
|
+
: `${(assetData.size / 1024).toFixed(1)} KB`
|
|
201
|
+
: null
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const fileMime = $derived(assetData?.mimeType || null);
|
|
205
|
+
|
|
206
|
+
function downloadAsset() {
|
|
207
|
+
if (fileUrl) {
|
|
208
|
+
downloadFile(fileUrl, displayName);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function copyUrl() {
|
|
213
|
+
if (fileUrl) {
|
|
214
|
+
await copyUrlToClipboard(fileUrl);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
</script>
|
|
218
|
+
|
|
219
|
+
<!-- Hidden file input -->
|
|
220
|
+
<input
|
|
221
|
+
bind:this={fileInputRef}
|
|
222
|
+
type="file"
|
|
223
|
+
accept={acceptString}
|
|
224
|
+
style="display: none"
|
|
225
|
+
onchange={handleFileInputChange}
|
|
226
|
+
/>
|
|
227
|
+
|
|
228
|
+
{#if arrayItem}
|
|
229
|
+
<!-- Minimal row for array items -->
|
|
230
|
+
{#if value && value.asset}
|
|
231
|
+
<div class="flex items-center gap-3">
|
|
232
|
+
<div
|
|
233
|
+
class="bg-muted flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded"
|
|
234
|
+
>
|
|
235
|
+
{#if loadingAsset}
|
|
236
|
+
<div class="border-primary h-4 w-4 animate-spin rounded-full border-b-2"></div>
|
|
237
|
+
{:else}
|
|
238
|
+
<FileIcon size={18} class="text-muted-foreground" />
|
|
239
|
+
{/if}
|
|
240
|
+
</div>
|
|
241
|
+
<div class="min-w-0 flex-1">
|
|
242
|
+
<span class="truncate text-sm">{displayName}</span>
|
|
243
|
+
{#if fileSize}
|
|
244
|
+
<span class="text-muted-foreground text-xs"> - {fileSize}</span>
|
|
245
|
+
{/if}
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
{:else}
|
|
249
|
+
<span class="text-muted-foreground text-sm">No file</span>
|
|
250
|
+
{/if}
|
|
251
|
+
{:else if compact}
|
|
252
|
+
<!-- Compact mode for arrays -->
|
|
253
|
+
{#if value && value.asset}
|
|
254
|
+
<div class="border-border flex items-center gap-3 rounded-md border p-2 {validationClasses}">
|
|
255
|
+
<div
|
|
256
|
+
class="bg-muted flex h-10 w-10 flex-shrink-0 items-center justify-center overflow-hidden rounded"
|
|
257
|
+
>
|
|
258
|
+
{#if loadingAsset}
|
|
259
|
+
<div class="border-primary h-4 w-4 animate-spin rounded-full border-b-2"></div>
|
|
260
|
+
{:else}
|
|
261
|
+
<FileIcon size={20} class="text-muted-foreground" />
|
|
262
|
+
{/if}
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<div class="flex-1 overflow-hidden">
|
|
266
|
+
<p class="truncate text-sm font-medium">{displayName}</p>
|
|
267
|
+
{#if fileSize || fileMime}
|
|
268
|
+
<p class="text-muted-foreground text-xs">
|
|
269
|
+
{fileMime || ''}{fileSize ? ` - ${fileSize}` : ''}
|
|
270
|
+
</p>
|
|
271
|
+
{/if}
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
{#if !readonly}
|
|
275
|
+
<DropdownMenu>
|
|
276
|
+
<DropdownMenuTrigger>
|
|
277
|
+
<Button variant="ghost" size="sm" class="h-8 w-8 p-0">
|
|
278
|
+
<Ellipsis size={16} />
|
|
279
|
+
</Button>
|
|
280
|
+
</DropdownMenuTrigger>
|
|
281
|
+
<DropdownMenuContent align="end">
|
|
282
|
+
<DropdownMenuGroup>
|
|
283
|
+
<DropdownMenuItem onclick={openFileDialog} disabled={isUploading}>
|
|
284
|
+
<Upload size={16} />
|
|
285
|
+
Replace
|
|
286
|
+
</DropdownMenuItem>
|
|
287
|
+
<DropdownMenuItem
|
|
288
|
+
onclick={() => {
|
|
289
|
+
showAssetBrowser = true;
|
|
290
|
+
}}
|
|
291
|
+
disabled={isUploading}
|
|
292
|
+
>
|
|
293
|
+
<FileIcon size={16} />
|
|
294
|
+
Browse media
|
|
295
|
+
</DropdownMenuItem>
|
|
296
|
+
</DropdownMenuGroup>
|
|
297
|
+
<DropdownMenuSeparator />
|
|
298
|
+
<DropdownMenuItem
|
|
299
|
+
onclick={removeFile}
|
|
300
|
+
disabled={isUploading}
|
|
301
|
+
class="text-destructive focus:text-destructive"
|
|
302
|
+
>
|
|
303
|
+
<CircleX size={16} />
|
|
304
|
+
Clear field
|
|
305
|
+
</DropdownMenuItem>
|
|
306
|
+
</DropdownMenuContent>
|
|
307
|
+
</DropdownMenu>
|
|
308
|
+
{/if}
|
|
309
|
+
</div>
|
|
310
|
+
{:else}
|
|
311
|
+
<Button
|
|
312
|
+
variant="outline"
|
|
313
|
+
class="w-full justify-start"
|
|
314
|
+
onclick={openFileDialog}
|
|
315
|
+
disabled={isUploading || readonly}
|
|
316
|
+
type="button"
|
|
317
|
+
>
|
|
318
|
+
{#if isUploading}
|
|
319
|
+
<div class="border-primary mr-2 h-4 w-4 animate-spin rounded-full border-b-2"></div>
|
|
320
|
+
Uploading...
|
|
321
|
+
{:else}
|
|
322
|
+
<Upload size={16} class="mr-2" />
|
|
323
|
+
Upload File
|
|
324
|
+
{/if}
|
|
325
|
+
</Button>
|
|
326
|
+
{/if}
|
|
327
|
+
{:else}
|
|
328
|
+
<!-- Full mode -->
|
|
329
|
+
{#if value && value.asset}
|
|
330
|
+
<div class="border-border overflow-hidden rounded-md border {validationClasses}">
|
|
331
|
+
<!-- File info card -->
|
|
332
|
+
<div class="flex items-center gap-4 p-4">
|
|
333
|
+
<div
|
|
334
|
+
class="bg-muted flex h-14 w-14 flex-shrink-0 items-center justify-center rounded-lg"
|
|
335
|
+
>
|
|
336
|
+
{#if loadingAsset}
|
|
337
|
+
<div class="border-primary h-6 w-6 animate-spin rounded-full border-b-2"></div>
|
|
338
|
+
{:else}
|
|
339
|
+
<FileIcon size={28} class="text-muted-foreground" />
|
|
340
|
+
{/if}
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<div class="min-w-0 flex-1">
|
|
344
|
+
<p class="truncate text-sm font-medium">{displayName}</p>
|
|
345
|
+
<div class="text-muted-foreground flex items-center gap-2 text-xs">
|
|
346
|
+
{#if fileMime}
|
|
347
|
+
<span>{fileMime}</span>
|
|
348
|
+
{/if}
|
|
349
|
+
{#if fileSize}
|
|
350
|
+
<span>{fileSize}</span>
|
|
351
|
+
{/if}
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<!-- Controls -->
|
|
356
|
+
{#if !readonly}
|
|
357
|
+
<DropdownMenu>
|
|
358
|
+
<DropdownMenuTrigger>
|
|
359
|
+
<Button variant="secondary" size="icon" class="h-8 w-8">
|
|
360
|
+
<Ellipsis size={16} />
|
|
361
|
+
</Button>
|
|
362
|
+
</DropdownMenuTrigger>
|
|
363
|
+
<DropdownMenuContent align="end">
|
|
364
|
+
<DropdownMenuGroup>
|
|
365
|
+
<DropdownMenuItem onclick={openFileDialog} disabled={isUploading}>
|
|
366
|
+
<Upload size={16} />
|
|
367
|
+
Replace
|
|
368
|
+
</DropdownMenuItem>
|
|
369
|
+
<DropdownMenuItem
|
|
370
|
+
onclick={() => {
|
|
371
|
+
showAssetBrowser = true;
|
|
372
|
+
}}
|
|
373
|
+
disabled={isUploading}
|
|
374
|
+
>
|
|
375
|
+
<FileIcon size={16} />
|
|
376
|
+
Browse media
|
|
377
|
+
</DropdownMenuItem>
|
|
378
|
+
</DropdownMenuGroup>
|
|
379
|
+
<DropdownMenuSeparator />
|
|
380
|
+
<DropdownMenuGroup>
|
|
381
|
+
<DropdownMenuItem onclick={downloadAsset} disabled={!fileUrl}>
|
|
382
|
+
<Download size={16} />
|
|
383
|
+
Download
|
|
384
|
+
</DropdownMenuItem>
|
|
385
|
+
<DropdownMenuItem onclick={copyUrl} disabled={!fileUrl}>
|
|
386
|
+
<Copy size={16} />
|
|
387
|
+
Copy URL
|
|
388
|
+
</DropdownMenuItem>
|
|
389
|
+
</DropdownMenuGroup>
|
|
390
|
+
<DropdownMenuSeparator />
|
|
391
|
+
<DropdownMenuItem
|
|
392
|
+
onclick={removeFile}
|
|
393
|
+
disabled={isUploading}
|
|
394
|
+
class="text-destructive focus:text-destructive"
|
|
395
|
+
>
|
|
396
|
+
<CircleX size={16} />
|
|
397
|
+
Clear field
|
|
398
|
+
</DropdownMenuItem>
|
|
399
|
+
</DropdownMenuContent>
|
|
400
|
+
</DropdownMenu>
|
|
401
|
+
{/if}
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
{:else}
|
|
405
|
+
<!-- Upload bar -->
|
|
406
|
+
<div
|
|
407
|
+
class="border-border flex h-10 items-center overflow-hidden rounded-md border transition-colors {validationClasses} {readonly
|
|
408
|
+
? ''
|
|
409
|
+
: isDragging
|
|
410
|
+
? 'bg-primary/5'
|
|
411
|
+
: ''}"
|
|
412
|
+
use:elementEvents={{
|
|
413
|
+
events: [
|
|
414
|
+
{ name: 'dragover', handler: handleDragOver },
|
|
415
|
+
{ name: 'drop', handler: handleDrop },
|
|
416
|
+
{ name: 'dragleave', handler: handleDragLeave }
|
|
417
|
+
]
|
|
418
|
+
}}
|
|
419
|
+
>
|
|
420
|
+
<div class="flex flex-1 items-center gap-2 px-3">
|
|
421
|
+
{#if isUploading}
|
|
422
|
+
<div
|
|
423
|
+
class="border-primary h-4 w-4 animate-spin rounded-full border-2 border-t-transparent"
|
|
424
|
+
></div>
|
|
425
|
+
<span class="text-muted-foreground text-sm">Uploading...</span>
|
|
426
|
+
{:else}
|
|
427
|
+
<FileIcon size={16} class="text-muted-foreground" />
|
|
428
|
+
<span class="text-muted-foreground text-sm">
|
|
429
|
+
{readonly ? 'No file' : isDragging ? 'Drop file here' : 'Drag or select a file'}
|
|
430
|
+
</span>
|
|
431
|
+
{/if}
|
|
432
|
+
</div>
|
|
433
|
+
|
|
434
|
+
<div class="flex items-center gap-1 pr-2">
|
|
435
|
+
<button
|
|
436
|
+
onclick={openFileDialog}
|
|
437
|
+
disabled={isUploading || readonly}
|
|
438
|
+
type="button"
|
|
439
|
+
class="text-muted-foreground hover:text-foreground flex items-center gap-1 px-2 py-1 text-sm transition-colors disabled:opacity-50"
|
|
440
|
+
>
|
|
441
|
+
<Upload size={14} />
|
|
442
|
+
Upload
|
|
443
|
+
</button>
|
|
444
|
+
<button
|
|
445
|
+
disabled={isUploading || readonly}
|
|
446
|
+
type="button"
|
|
447
|
+
onclick={() => {
|
|
448
|
+
showAssetBrowser = true;
|
|
449
|
+
}}
|
|
450
|
+
class="text-muted-foreground hover:text-foreground flex items-center gap-1 px-2 py-1 text-sm transition-colors disabled:opacity-50"
|
|
451
|
+
>
|
|
452
|
+
<FileIcon size={14} />
|
|
453
|
+
Select
|
|
454
|
+
</button>
|
|
455
|
+
</div>
|
|
456
|
+
</div>
|
|
457
|
+
{/if}
|
|
458
|
+
{/if}
|
|
459
|
+
|
|
460
|
+
{#if uploadError}
|
|
461
|
+
<p class="text-destructive mt-2 text-sm">{uploadError}</p>
|
|
462
|
+
{/if}
|
|
463
|
+
|
|
464
|
+
{#if field.accept}
|
|
465
|
+
<p class="text-muted-foreground mt-1 text-xs">
|
|
466
|
+
Accepted: {field.accept.join(', ')}
|
|
467
|
+
</p>
|
|
468
|
+
{/if}
|
|
469
|
+
|
|
470
|
+
<!-- Asset Browser Modal -->
|
|
471
|
+
<AssetBrowserModal
|
|
472
|
+
bind:open={showAssetBrowser}
|
|
473
|
+
onOpenChange={(v) => (showAssetBrowser = v)}
|
|
474
|
+
assetTypeFilter="file"
|
|
475
|
+
onSelect={(asset) => {
|
|
476
|
+
const fileValue: FileValue = {
|
|
477
|
+
_type: 'file',
|
|
478
|
+
asset: {
|
|
479
|
+
_type: 'reference',
|
|
480
|
+
_ref: asset.id
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
onUpdate(fileValue);
|
|
484
|
+
}}
|
|
485
|
+
/>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FileValue } from '../../../types/asset.js';
|
|
2
|
+
import type { FileField as FileFieldType } from '../../../types/schemas.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
field: FileFieldType;
|
|
5
|
+
value: FileValue | null;
|
|
6
|
+
validationClasses?: string;
|
|
7
|
+
onUpdate: (value: FileValue | null) => void;
|
|
8
|
+
schemaType?: string;
|
|
9
|
+
fieldPath?: string;
|
|
10
|
+
readonly?: boolean;
|
|
11
|
+
compact?: boolean;
|
|
12
|
+
arrayItem?: boolean;
|
|
13
|
+
organizationId?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const FileField: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type FileField = ReturnType<typeof FileField>;
|
|
17
|
+
export default FileField;
|
|
18
|
+
//# sourceMappingURL=FileField.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/FileField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAiBxE,UAAU,KAAK;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAgaF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
<Command.Empty>
|
|
215
215
|
<div class="flex flex-col items-center gap-2 py-4">
|
|
216
216
|
<p class="text-muted-foreground text-sm">
|
|
217
|
-
No {pluralize(targetType)} found
|
|
217
|
+
No {pluralize(targetType || '')} found
|
|
218
218
|
</p>
|
|
219
219
|
<Button size="sm" onclick={createNewDocument} disabled={creating} class="gap-1">
|
|
220
220
|
<PlusIcon class="h-3 w-3" />
|
|
@@ -254,7 +254,7 @@
|
|
|
254
254
|
</Command.Group>
|
|
255
255
|
{:else}
|
|
256
256
|
<Command.Empty>
|
|
257
|
-
No {pluralize(targetType)} available
|
|
257
|
+
No {pluralize(targetType || '')} available
|
|
258
258
|
</Command.Empty>
|
|
259
259
|
{/if}
|
|
260
260
|
</Command.List>
|
|
@@ -4,6 +4,7 @@ export { default as NumberField } from '../admin/fields/NumberField.svelte';
|
|
|
4
4
|
export { default as BooleanField } from '../admin/fields/BooleanField.svelte';
|
|
5
5
|
export { default as SlugField } from '../admin/fields/SlugField.svelte';
|
|
6
6
|
export { default as ImageField } from '../admin/fields/ImageField.svelte';
|
|
7
|
+
export { default as FileField } from '../admin/fields/FileField.svelte';
|
|
7
8
|
export { default as ArrayField } from '../admin/fields/ArrayField.svelte';
|
|
8
9
|
export { default as ReferenceField } from '../admin/fields/ReferenceField.svelte';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/components/fields/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,uCAAuC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/components/fields/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,uCAAuC,CAAC"}
|
|
@@ -5,5 +5,6 @@ export { default as NumberField } from '../admin/fields/NumberField.svelte';
|
|
|
5
5
|
export { default as BooleanField } from '../admin/fields/BooleanField.svelte';
|
|
6
6
|
export { default as SlugField } from '../admin/fields/SlugField.svelte';
|
|
7
7
|
export { default as ImageField } from '../admin/fields/ImageField.svelte';
|
|
8
|
+
export { default as FileField } from '../admin/fields/FileField.svelte';
|
|
8
9
|
export { default as ArrayField } from '../admin/fields/ArrayField.svelte';
|
|
9
10
|
export { default as ReferenceField } from '../admin/fields/ReferenceField.svelte';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/lib/graphql/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAkD,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/lib/graphql/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAkD,MAAM,kBAAkB,CAAC;AAqWnG,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,CA2FvE"}
|
package/dist/graphql/schema.js
CHANGED
|
@@ -18,6 +18,8 @@ function getGraphQLType(field, schemaTypes, parentName = '') {
|
|
|
18
18
|
return 'Boolean';
|
|
19
19
|
case 'image':
|
|
20
20
|
return 'Image';
|
|
21
|
+
case 'file':
|
|
22
|
+
return 'FileAsset';
|
|
21
23
|
case 'array':
|
|
22
24
|
return handleArrayField(field, schemaTypes, parentName);
|
|
23
25
|
case 'object':
|
|
@@ -234,7 +236,8 @@ function getInputFieldType(field, _schemaTypes, _parentName) {
|
|
|
234
236
|
case 'reference':
|
|
235
237
|
return 'String'; // Reference IDs
|
|
236
238
|
case 'image':
|
|
237
|
-
|
|
239
|
+
case 'file':
|
|
240
|
+
return 'JSON'; // Complex asset references
|
|
238
241
|
case 'array':
|
|
239
242
|
// For arrays, we'll use JSON for simplicity
|
|
240
243
|
// In a more advanced implementation, you'd create specific input types
|
|
@@ -323,6 +326,17 @@ export function generateGraphQLSchema(schemaTypes) {
|
|
|
323
326
|
type ImageAsset {
|
|
324
327
|
_ref: String!
|
|
325
328
|
_type: String!
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
type FileAsset {
|
|
332
|
+
_type: String!
|
|
333
|
+
asset: FileAssetRef
|
|
334
|
+
url: String
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
type FileAssetRef {
|
|
338
|
+
_ref: String!
|
|
339
|
+
_type: String!
|
|
326
340
|
}`;
|
|
327
341
|
const scalarDefs = `# JSON scalar for flexible data
|
|
328
342
|
scalar JSON`;
|
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/lib/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAA8B,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,OAAO,EAAa,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlE,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,YAAY,CAAC,EAAE,GAAG,CACjB,MAAM,EACN;QAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAC7E,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/lib/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAA8B,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,OAAO,EAAa,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlE,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,YAAY,CAAC,EAAE,GAAG,CACjB,MAAM,EACN;QAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAC7E,CAAC;CACF;AAiFD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAiLvD"}
|
package/dist/hooks.js
CHANGED
|
@@ -6,6 +6,7 @@ import { createCMS } from './engine.js';
|
|
|
6
6
|
import { createLocalAPI } from './local-api/index.js';
|
|
7
7
|
let cmsInstances = null;
|
|
8
8
|
let schemaError = null;
|
|
9
|
+
let initPromise = null;
|
|
9
10
|
/**
|
|
10
11
|
* Check if schemas are dirty (changed via Vite HMR)
|
|
11
12
|
* Vite plugin sets a global flag when schema files change
|
|
@@ -77,7 +78,13 @@ export function createCMSHook(config) {
|
|
|
77
78
|
// Note: In dev mode, /storage/ might be accessible via Vite dev server
|
|
78
79
|
// In production, only /static/ folder is served - /storage/ is private
|
|
79
80
|
// Initialize CMS instances once at application startup
|
|
81
|
+
// Use a promise lock to prevent concurrent requests from racing initialization
|
|
82
|
+
if (initPromise) {
|
|
83
|
+
await initPromise;
|
|
84
|
+
}
|
|
80
85
|
if (!cmsInstances) {
|
|
86
|
+
let resolveInit;
|
|
87
|
+
initPromise = new Promise((r) => (resolveInit = r));
|
|
81
88
|
cmsLogger.info('[CMS]', 'Initializing...');
|
|
82
89
|
const databaseAdapter = config.database;
|
|
83
90
|
// Use the storage adapter from config, or create the default local one.
|
|
@@ -175,11 +182,15 @@ export function createCMSHook(config) {
|
|
|
175
182
|
graphqlSettings,
|
|
176
183
|
pluginRoutes
|
|
177
184
|
};
|
|
185
|
+
resolveInit();
|
|
178
186
|
}
|
|
179
187
|
else if (checkSchemasDirty()) {
|
|
180
188
|
// HMR: Schemas changed - reset instances so full re-initialization
|
|
181
189
|
// runs on the next request with fresh config from Vite
|
|
182
190
|
cmsLogger.info('[CMS]', 'Schema change detected, re-initializing...');
|
|
191
|
+
if (cmsInstances?.config.cache) {
|
|
192
|
+
cmsInstances.config.cache.flush();
|
|
193
|
+
}
|
|
183
194
|
cmsInstances = null;
|
|
184
195
|
schemaError = null;
|
|
185
196
|
return resolve(event); // Let the next request trigger full init
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CacheAdapter } from '../interfaces/cache.js';
|
|
2
|
+
export interface InMemoryCacheOptions {
|
|
3
|
+
/** Maximum number of entries. Oldest entries are evicted when exceeded. Defaults to 1000. */
|
|
4
|
+
maxSize?: number;
|
|
5
|
+
/** Default TTL in seconds. No expiry if omitted. */
|
|
6
|
+
defaultTTL?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class InMemoryCacheAdapter implements CacheAdapter {
|
|
9
|
+
readonly name = "in-memory";
|
|
10
|
+
private store;
|
|
11
|
+
private maxSize;
|
|
12
|
+
private defaultTTL;
|
|
13
|
+
constructor(options?: InMemoryCacheOptions);
|
|
14
|
+
get<T>(key: string): Promise<T | null>;
|
|
15
|
+
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
16
|
+
delete(key: string): Promise<void>;
|
|
17
|
+
invalidateByPrefix(prefix: string): Promise<void>;
|
|
18
|
+
flush(): Promise<void>;
|
|
19
|
+
isHealthy(): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=in-memory-cache-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-memory-cache-adapter.d.ts","sourceRoot":"","sources":["../../../../src/lib/cache/adapters/in-memory-cache-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOxD,MAAM,WAAW,oBAAoB;IACpC,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,oBAAqB,YAAW,YAAY;IACxD,QAAQ,CAAC,IAAI,eAAe;IAE5B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAqB;gBAE3B,OAAO,GAAE,oBAAyB;IAKxC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAYtC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB1D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAGnC"}
|