@lucashw68/nsdb 1.0.0-rc.2
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/CHANGELOG.md +34 -0
- package/GET_STARTED.md +709 -0
- package/LICENSE +21 -0
- package/README.md +159 -0
- package/cli/index.js +83 -0
- package/helpers/args.js +22 -0
- package/helpers/config.js +142 -0
- package/helpers/generated.js +48 -0
- package/helpers/io.js +39 -0
- package/helpers/metadata.js +19 -0
- package/helpers/names.js +16 -0
- package/helpers/relations.js +101 -0
- package/helpers/shell.js +15 -0
- package/helpers/tables.js +79 -0
- package/helpers/ts.js +37 -0
- package/module.ts +151 -0
- package/nsdb.config.example.mjs +39 -0
- package/nsdb.config.example.ts +42 -0
- package/package.json +114 -0
- package/runtime/components/Form/NsdbRelationSelect.vue +258 -0
- package/runtime/components/NsdbForm.vue +865 -0
- package/runtime/components/NsdbList.vue +961 -0
- package/runtime/composables/useNsdbProfile.ts +119 -0
- package/runtime/composables/useNsdbSchemas.ts +176 -0
- package/runtime/composables/useSupabaseApi.ts +177 -0
- package/runtime/composables/useSupabaseApiStorage.ts +337 -0
- package/runtime/composables/useSupabaseModels.ts +412 -0
- package/runtime/query.ts +126 -0
- package/runtime/stores/createDbStore.ts +439 -0
- package/runtime/stores/createSingletonDbStore.ts +67 -0
- package/runtime/utils/dataFreshness.ts +47 -0
- package/runtime/utils/storage.ts +41 -0
- package/scripts/clear.js +64 -0
- package/scripts/generate-composables.js +100 -0
- package/scripts/generate-enums.js +106 -0
- package/scripts/generate-metadata.js +165 -0
- package/scripts/generate-models.js +164 -0
- package/scripts/generate-schemas.js +443 -0
- package/scripts/generate-stores.js +90 -0
- package/scripts/generate-types.js +196 -0
- package/scripts/init.js +225 -0
- package/templates/model.template.ts +48 -0
- package/templates/schema.template.ts +13 -0
- package/templates/useNsdbModel.template.ts +9 -0
- package/types/config.ts +50 -0
- package/types/entities.ts +66 -0
- package/types/index.ts +14 -0
- package/types/list.ts +78 -0
- package/types/model.ts +57 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { useSupabaseClient } from '#imports'
|
|
2
|
+
import type { OrderDirection } from '@lucashw68/nsdb/types/list'
|
|
3
|
+
import {
|
|
4
|
+
applySearchFilter,
|
|
5
|
+
joinPath,
|
|
6
|
+
normalizeBucketName,
|
|
7
|
+
normalizeDirectoryPath,
|
|
8
|
+
normalizeFilePath,
|
|
9
|
+
normalizePath,
|
|
10
|
+
} from '../utils/storage'
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
applySearchFilter,
|
|
14
|
+
joinPath,
|
|
15
|
+
normalizeBucketName,
|
|
16
|
+
normalizeDirectoryPath,
|
|
17
|
+
normalizeFilePath,
|
|
18
|
+
normalizePath,
|
|
19
|
+
} from '../utils/storage'
|
|
20
|
+
|
|
21
|
+
export type StorageFileBody = File | Blob | ArrayBuffer | ArrayBufferView | FormData | string
|
|
22
|
+
|
|
23
|
+
export type StorageOrderBy =
|
|
24
|
+
| 'name'
|
|
25
|
+
| 'created_at'
|
|
26
|
+
| 'updated_at'
|
|
27
|
+
| 'last_accessed_at'
|
|
28
|
+
|
|
29
|
+
export type StorageImageResizeMode = 'cover' | 'contain' | 'fill'
|
|
30
|
+
export type StorageImageFormat = 'origin' | 'webp' | 'png' | 'jpeg'
|
|
31
|
+
|
|
32
|
+
export interface StorageImageTransformOptions {
|
|
33
|
+
width?: number
|
|
34
|
+
height?: number
|
|
35
|
+
resize?: StorageImageResizeMode
|
|
36
|
+
quality?: number
|
|
37
|
+
format?: StorageImageFormat
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface StorageListOptions {
|
|
41
|
+
path?: string
|
|
42
|
+
limit?: number
|
|
43
|
+
offset?: number
|
|
44
|
+
orderBy?: StorageOrderBy
|
|
45
|
+
orderDirection?: OrderDirection
|
|
46
|
+
search?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface StorageWriteOptions {
|
|
50
|
+
cacheControl?: string
|
|
51
|
+
contentType?: string
|
|
52
|
+
metadata?: Record<string, unknown>
|
|
53
|
+
upsert?: boolean
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface StorageDownloadOptions {
|
|
57
|
+
transform?: StorageImageTransformOptions
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface StoragePublicUrlOptions {
|
|
61
|
+
download?: boolean | string
|
|
62
|
+
transform?: StorageImageTransformOptions
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface StorageSignedUrlOptions {
|
|
66
|
+
download?: boolean | string
|
|
67
|
+
transform?: StorageImageTransformOptions
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface StorageCreateBucketOptions {
|
|
71
|
+
public?: boolean
|
|
72
|
+
allowedMimeTypes?: string[]
|
|
73
|
+
fileSizeLimit?: number | string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface StorageApiSuccess<T> {
|
|
77
|
+
success: true
|
|
78
|
+
data: T
|
|
79
|
+
error: undefined
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface StorageApiFailure<T> {
|
|
83
|
+
success: false
|
|
84
|
+
data: T
|
|
85
|
+
error: unknown
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type StorageApiResponse<T> = StorageApiSuccess<T> | StorageApiFailure<T>
|
|
89
|
+
|
|
90
|
+
const DEFAULT_LIST_OPTIONS: Required<Pick<StorageListOptions, 'path' | 'limit' | 'offset' | 'orderBy' | 'orderDirection'>> = {
|
|
91
|
+
path: '',
|
|
92
|
+
limit: 100,
|
|
93
|
+
offset: 0,
|
|
94
|
+
orderBy: 'name',
|
|
95
|
+
orderDirection: 'asc',
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function handleStorageResponse<T>(
|
|
99
|
+
payload: { data: T | null; error: unknown },
|
|
100
|
+
context: string,
|
|
101
|
+
fallbackData: T
|
|
102
|
+
): StorageApiResponse<T> {
|
|
103
|
+
const { data, error } = payload
|
|
104
|
+
|
|
105
|
+
if (error) {
|
|
106
|
+
console.error(`❌ [storage:${context}]`, error)
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
error,
|
|
110
|
+
data: fallbackData,
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
success: true,
|
|
116
|
+
error: undefined,
|
|
117
|
+
data: (data ?? fallbackData) as T,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizeBucketOptions(options: StorageCreateBucketOptions = {}) {
|
|
122
|
+
return {
|
|
123
|
+
public: options.public ?? false,
|
|
124
|
+
allowedMimeTypes: options.allowedMimeTypes,
|
|
125
|
+
fileSizeLimit: options.fileSizeLimit,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const useSupabaseApiStorage = () => {
|
|
130
|
+
const supabaseClient = useSupabaseClient?.()
|
|
131
|
+
|
|
132
|
+
if (!supabaseClient) {
|
|
133
|
+
throw new Error('[nsdb] Supabase client not found. Install @nuxtjs/supabase.')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function bucket(bucketName: string) {
|
|
137
|
+
return supabaseClient.storage.from(normalizeBucketName(bucketName))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Liste les fichiers et dossiers d'un bucket.
|
|
142
|
+
* La recherche est locale car Supabase Storage ne fournit pas de recherche texte native.
|
|
143
|
+
*/
|
|
144
|
+
async function list<T extends { name?: string } = any>(bucketName: string, options: StorageListOptions = {}) {
|
|
145
|
+
const listOptions = { ...DEFAULT_LIST_OPTIONS, ...options }
|
|
146
|
+
const directoryPath = normalizeDirectoryPath(listOptions.path)
|
|
147
|
+
|
|
148
|
+
const { data, error } = await bucket(bucketName).list(directoryPath, {
|
|
149
|
+
limit: listOptions.limit,
|
|
150
|
+
offset: listOptions.offset,
|
|
151
|
+
sortBy: {
|
|
152
|
+
column: listOptions.orderBy,
|
|
153
|
+
order: listOptions.orderDirection,
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
const filteredData = applySearchFilter((data ?? []) as unknown as T[], listOptions.search)
|
|
158
|
+
return handleStorageResponse<T[]>({ data: filteredData, error }, `LIST ${bucketName}/${directoryPath}`, [])
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Upload un nouveau fichier. Utiliser update() pour remplacer explicitement un fichier existant.
|
|
163
|
+
*/
|
|
164
|
+
async function upload<T = any>(
|
|
165
|
+
bucketName: string,
|
|
166
|
+
path: string,
|
|
167
|
+
fileBody: StorageFileBody,
|
|
168
|
+
options: StorageWriteOptions = {}
|
|
169
|
+
) {
|
|
170
|
+
const filePath = normalizeFilePath(path)
|
|
171
|
+
const { data, error } = await bucket(bucketName).upload(filePath, fileBody, options as any)
|
|
172
|
+
return handleStorageResponse<T>({ data: data as T | null, error }, `UPLOAD ${bucketName}/${filePath}`, null as T)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Remplace le contenu d'un fichier existant sans changer son chemin.
|
|
177
|
+
*/
|
|
178
|
+
async function update<T = any>(
|
|
179
|
+
bucketName: string,
|
|
180
|
+
path: string,
|
|
181
|
+
fileBody: StorageFileBody,
|
|
182
|
+
options: Omit<StorageWriteOptions, 'upsert'> = {}
|
|
183
|
+
) {
|
|
184
|
+
const filePath = normalizeFilePath(path)
|
|
185
|
+
const { data, error } = await bucket(bucketName).update(filePath, fileBody, options as any)
|
|
186
|
+
return handleStorageResponse<T>({ data: data as T | null, error }, `UPDATE ${bucketName}/${filePath}`, null as T)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function download(bucketName: string, path: string, options: StorageDownloadOptions = {}) {
|
|
190
|
+
const filePath = normalizeFilePath(path)
|
|
191
|
+
const { data, error } = await bucket(bucketName).download(filePath, options as any)
|
|
192
|
+
return handleStorageResponse<Blob>({ data, error }, `DOWNLOAD ${bucketName}/${filePath}`, null as unknown as Blob)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function remove<T = any>(bucketName: string, paths: string | string[]) {
|
|
196
|
+
const filePaths = (Array.isArray(paths) ? paths : [paths]).map(normalizeFilePath)
|
|
197
|
+
const { data, error } = await bucket(bucketName).remove(filePaths)
|
|
198
|
+
return handleStorageResponse<T[]>({ data: data as T[] | null, error }, `REMOVE ${bucketName}`, [])
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getPublicUrl(bucketName: string, path: string, options: StoragePublicUrlOptions = {}) {
|
|
202
|
+
const filePath = normalizeFilePath(path)
|
|
203
|
+
const { data } = bucket(bucketName).getPublicUrl(filePath, options as any)
|
|
204
|
+
return data.publicUrl
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function createSignedUrl(
|
|
208
|
+
bucketName: string,
|
|
209
|
+
path: string,
|
|
210
|
+
expiresIn = 60,
|
|
211
|
+
options: StorageSignedUrlOptions = {}
|
|
212
|
+
) {
|
|
213
|
+
const filePath = normalizeFilePath(path)
|
|
214
|
+
const { data, error } = await bucket(bucketName).createSignedUrl(filePath, expiresIn, options as any)
|
|
215
|
+
return handleStorageResponse<{ signedUrl: string }>(
|
|
216
|
+
{ data, error },
|
|
217
|
+
`SIGNED_URL ${bucketName}/${filePath}`,
|
|
218
|
+
null as unknown as { signedUrl: string },
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function createSignedUrls(
|
|
223
|
+
bucketName: string,
|
|
224
|
+
paths: string[],
|
|
225
|
+
expiresIn = 60,
|
|
226
|
+
options: StorageSignedUrlOptions = {}
|
|
227
|
+
) {
|
|
228
|
+
const filePaths = paths.map(normalizeFilePath)
|
|
229
|
+
const { data, error } = await bucket(bucketName).createSignedUrls(filePaths, expiresIn, options as any)
|
|
230
|
+
return handleStorageResponse<Array<{ path: string; signedUrl: string }>>(
|
|
231
|
+
{ data: data as Array<{ path: string; signedUrl: string }> | null, error },
|
|
232
|
+
`SIGNED_URLS ${bucketName}`,
|
|
233
|
+
[],
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function createSignedUploadUrl(bucketName: string, path: string) {
|
|
238
|
+
const filePath = normalizeFilePath(path)
|
|
239
|
+
const { data, error } = await bucket(bucketName).createSignedUploadUrl(filePath)
|
|
240
|
+
return handleStorageResponse<{ signedUrl: string; path: string; token: string }>(
|
|
241
|
+
{ data, error },
|
|
242
|
+
`SIGNED_UPLOAD_URL ${bucketName}/${filePath}`,
|
|
243
|
+
null as unknown as { signedUrl: string; path: string; token: string },
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function uploadToSignedUrl<T = any>(
|
|
248
|
+
bucketName: string,
|
|
249
|
+
path: string,
|
|
250
|
+
token: string,
|
|
251
|
+
fileBody: StorageFileBody
|
|
252
|
+
) {
|
|
253
|
+
const filePath = normalizeFilePath(path)
|
|
254
|
+
const { data, error } = await bucket(bucketName).uploadToSignedUrl(filePath, token, fileBody as any)
|
|
255
|
+
return handleStorageResponse<T>({ data: data as T | null, error }, `UPLOAD_SIGNED_URL ${bucketName}/${filePath}`, null as T)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function move<T = any>(bucketName: string, fromPath: string, toPath: string) {
|
|
259
|
+
const normalizedFromPath = normalizeFilePath(fromPath)
|
|
260
|
+
const normalizedToPath = normalizeFilePath(toPath)
|
|
261
|
+
const { data, error } = await bucket(bucketName).move(normalizedFromPath, normalizedToPath)
|
|
262
|
+
return handleStorageResponse<T>(
|
|
263
|
+
{ data: data as T | null, error },
|
|
264
|
+
`MOVE ${bucketName} ${normalizedFromPath} -> ${normalizedToPath}`,
|
|
265
|
+
null as T,
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function copy<T = any>(bucketName: string, fromPath: string, toPath: string) {
|
|
270
|
+
const normalizedFromPath = normalizeFilePath(fromPath)
|
|
271
|
+
const normalizedToPath = normalizeFilePath(toPath)
|
|
272
|
+
const { data, error } = await bucket(bucketName).copy(normalizedFromPath, normalizedToPath)
|
|
273
|
+
return handleStorageResponse<T>(
|
|
274
|
+
{ data: data as T | null, error },
|
|
275
|
+
`COPY ${bucketName} ${normalizedFromPath} -> ${normalizedToPath}`,
|
|
276
|
+
null as T,
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function listBuckets<T = any>() {
|
|
281
|
+
const { data, error } = await supabaseClient.storage.listBuckets()
|
|
282
|
+
return handleStorageResponse<T[]>({ data: data as T[] | null, error }, 'LIST_BUCKETS', [])
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function getBucket<T = any>(bucketName: string) {
|
|
286
|
+
const normalizedBucketName = normalizeBucketName(bucketName)
|
|
287
|
+
const { data, error } = await supabaseClient.storage.getBucket(normalizedBucketName)
|
|
288
|
+
return handleStorageResponse<T>({ data: data as T | null, error }, `GET_BUCKET ${normalizedBucketName}`, null as T)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function createBucket<T = any>(bucketName: string, options: StorageCreateBucketOptions = {}) {
|
|
292
|
+
const normalizedBucketName = normalizeBucketName(bucketName)
|
|
293
|
+
const { data, error } = await supabaseClient.storage.createBucket(normalizedBucketName, normalizeBucketOptions(options))
|
|
294
|
+
return handleStorageResponse<T>({ data: data as T | null, error }, `CREATE_BUCKET ${normalizedBucketName}`, null as T)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function updateBucket<T = any>(bucketName: string, options: StorageCreateBucketOptions) {
|
|
298
|
+
const normalizedBucketName = normalizeBucketName(bucketName)
|
|
299
|
+
const { data, error } = await supabaseClient.storage.updateBucket(normalizedBucketName, normalizeBucketOptions(options))
|
|
300
|
+
return handleStorageResponse<T>({ data: data as T | null, error }, `UPDATE_BUCKET ${normalizedBucketName}`, null as T)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function deleteBucket<T = any>(bucketName: string) {
|
|
304
|
+
const normalizedBucketName = normalizeBucketName(bucketName)
|
|
305
|
+
const { data, error } = await supabaseClient.storage.deleteBucket(normalizedBucketName)
|
|
306
|
+
return handleStorageResponse<T>({ data: data as T | null, error }, `DELETE_BUCKET ${normalizedBucketName}`, null as T)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function emptyBucket<T = any>(bucketName: string) {
|
|
310
|
+
const normalizedBucketName = normalizeBucketName(bucketName)
|
|
311
|
+
const { data, error } = await supabaseClient.storage.emptyBucket(normalizedBucketName)
|
|
312
|
+
return handleStorageResponse<T>({ data: data as T | null, error }, `EMPTY_BUCKET ${normalizedBucketName}`, null as T)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
list,
|
|
317
|
+
upload,
|
|
318
|
+
update,
|
|
319
|
+
download,
|
|
320
|
+
remove,
|
|
321
|
+
getPublicUrl,
|
|
322
|
+
createSignedUrl,
|
|
323
|
+
createSignedUrls,
|
|
324
|
+
createSignedUploadUrl,
|
|
325
|
+
uploadToSignedUrl,
|
|
326
|
+
move,
|
|
327
|
+
copy,
|
|
328
|
+
listBuckets,
|
|
329
|
+
getBucket,
|
|
330
|
+
createBucket,
|
|
331
|
+
updateBucket,
|
|
332
|
+
deleteBucket,
|
|
333
|
+
emptyBucket,
|
|
334
|
+
joinPath,
|
|
335
|
+
normalizePath,
|
|
336
|
+
}
|
|
337
|
+
}
|