@flaredev/core 0.4.0 → 0.4.1
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.
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/resource/fields.ts
|
|
2
2
|
var STRING_FORMATS = ["email", "url", "tel", "domain", "country", "color", "slug"];
|
|
3
3
|
var FILE_CATEGORIES = {
|
|
4
|
+
// For a field that holds whatever someone has: a drive, an attachment, a backup.
|
|
5
|
+
// Nothing is checked beyond the size limit, so don't reach for it out of convenience.
|
|
6
|
+
any: ["*/*"],
|
|
4
7
|
image: ["image/png", "image/jpeg", "image/gif", "image/webp", "image/avif"],
|
|
5
8
|
pdf: ["application/pdf"],
|
|
6
9
|
video: ["video/*"],
|
|
@@ -17,7 +20,18 @@ var FILE_CATEGORIES = {
|
|
|
17
20
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
18
21
|
"application/vnd.oasis.opendocument.spreadsheet"
|
|
19
22
|
],
|
|
20
|
-
|
|
23
|
+
// Browsers disagree about what a .zip is — Windows sends x-zip-compressed — and an
|
|
24
|
+
// archive field that refuses an ordinary zip is no use, so the aliases are listed too.
|
|
25
|
+
archive: [
|
|
26
|
+
"application/zip",
|
|
27
|
+
"application/x-zip-compressed",
|
|
28
|
+
"application/x-zip",
|
|
29
|
+
"application/gzip",
|
|
30
|
+
"application/x-gzip",
|
|
31
|
+
"application/x-7z-compressed",
|
|
32
|
+
"application/vnd.rar",
|
|
33
|
+
"application/x-rar-compressed"
|
|
34
|
+
]
|
|
21
35
|
};
|
|
22
36
|
function mimeTypesFor(accept) {
|
|
23
37
|
return [...new Set(accept.flatMap((category) => FILE_CATEGORIES[category]))];
|
|
@@ -87,6 +101,13 @@ function pluralize(value) {
|
|
|
87
101
|
|
|
88
102
|
// src/resource/define.ts
|
|
89
103
|
var RESERVED_FIELD_NAMES = ["id", "createdAt", "updatedAt"];
|
|
104
|
+
function clientResource(resource) {
|
|
105
|
+
const { hooks, computed, $types, ...rest } = resource;
|
|
106
|
+
void hooks;
|
|
107
|
+
void computed;
|
|
108
|
+
void $types;
|
|
109
|
+
return rest;
|
|
110
|
+
}
|
|
90
111
|
var ResourceDefinitionError = class extends Error {
|
|
91
112
|
constructor(resource, message) {
|
|
92
113
|
super(`Resource "${resource}": ${message}`);
|
|
@@ -385,6 +406,7 @@ export {
|
|
|
385
406
|
humanize,
|
|
386
407
|
pluralize,
|
|
387
408
|
RESERVED_FIELD_NAMES,
|
|
409
|
+
clientResource,
|
|
388
410
|
ResourceDefinitionError,
|
|
389
411
|
defaultFieldLabel,
|
|
390
412
|
defineResource,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as Field, C as CreateInput, U as UpdateInput, R as Resource, S as StoredField } from './policy-
|
|
2
|
-
export { B as BelongsToField, a as BooleanField, D as DEFAULT_FILE_MAX_BYTES,
|
|
1
|
+
import { F as Field, C as CreateInput, U as UpdateInput, R as Resource, S as StoredField } from './policy-DaKcWZ5g.js';
|
|
2
|
+
export { B as BelongsToField, a as BooleanField, b as ClientResource, D as DEFAULT_FILE_MAX_BYTES, c as DateField, d as DateTimeField, E as EnumField, e as FILE_CATEGORIES, f as FieldKind, g as FieldValue, h as FileCategory, i as FileField, j as FloatField, H as HasManyField, I as IntField, M as MultiSelectField, P as Policy, k as PolicyAction, l as PolicyConfig, m as PolicyError, n as RESERVED_FIELD_NAMES, o as RecordOf, p as ResourceConfig, q as ResourceDefinitionError, r as STRING_FORMATS, s as StringField, t as StringFormat, T as TextField, u as allowedActions, v as can, w as clientResource, x as columnName, y as defaultFieldLabel, z as definePolicy, A as defineResource, G as field, J as fileMaxBytes, K as mimeTypesFor, L as storedFields } from './policy-DaKcWZ5g.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -286,7 +286,7 @@ declare function fieldSchema(def: StoredField): z.ZodType;
|
|
|
286
286
|
* edit a record could attach another resource's private file and read it through theirs.
|
|
287
287
|
*/
|
|
288
288
|
declare const fileKeyPrefix: (resource: Pick<Resource, "table">, fieldKey: string) => string;
|
|
289
|
-
declare function createValidators<Fields extends Record<string, Field>>(resource: Resource<Fields>): ResourceValidators<Fields>;
|
|
289
|
+
declare function createValidators<Fields extends Record<string, Field>>(resource: Pick<Resource<Fields>, "fields" | "table">): ResourceValidators<Fields>;
|
|
290
290
|
|
|
291
291
|
/** Naming helpers shared by descriptors and the generator. */
|
|
292
292
|
/** "OrderItem" / "order item" / "order-item" → ["order", "item"] */
|
|
@@ -365,13 +365,13 @@ declare function formatValue(def: StoredField | {
|
|
|
365
365
|
*/
|
|
366
366
|
type FormValues = Record<string, string | boolean>;
|
|
367
367
|
/** Initial form state: the record's values (edit) or the descriptor's defaults (create). */
|
|
368
|
-
declare function initialFormValues(resource: Resource, record?: Record<string, unknown> | null): FormValues;
|
|
368
|
+
declare function initialFormValues(resource: Pick<Resource, "fields">, record?: Record<string, unknown> | null): FormValues;
|
|
369
369
|
/**
|
|
370
370
|
* API input from form state. Empty optional fields become null (clearing them);
|
|
371
371
|
* empty fields that have a default are omitted on create so the default applies;
|
|
372
372
|
* empty required fields are left for validation to report as "Required".
|
|
373
373
|
*/
|
|
374
|
-
declare function formValuesToInput(resource: Resource, values: FormValues, mode: "create" | "edit"): Record<string, unknown>;
|
|
374
|
+
declare function formValuesToInput(resource: Pick<Resource, "fields">, values: FormValues, mode: "create" | "edit"): Record<string, unknown>;
|
|
375
375
|
/** A multiselect's form state (a JSON array string) as the picked values. */
|
|
376
376
|
declare function parseMultiValue(raw: string | boolean | undefined): string[];
|
|
377
377
|
/** Field errors keyed by field name, from validation or API issues (first message wins). */
|
|
@@ -439,6 +439,8 @@ interface Fake {
|
|
|
439
439
|
email(name?: string): string;
|
|
440
440
|
company(): string;
|
|
441
441
|
jobTitle(): string;
|
|
442
|
+
/** "Brushed steel desk lamp", or a digital one: "Starter kit licence". */
|
|
443
|
+
product(digital?: boolean): string;
|
|
442
444
|
city(): string;
|
|
443
445
|
/** ISO 3166-1 alpha-2, e.g. "UG". */
|
|
444
446
|
country(): string;
|
|
@@ -507,6 +509,6 @@ interface SeedContext {
|
|
|
507
509
|
type Seed = (context: SeedContext) => void | Promise<void>;
|
|
508
510
|
declare function defineSeed(seed: Seed): Seed;
|
|
509
511
|
|
|
510
|
-
declare const FLARE_VERSION = "0.4.
|
|
512
|
+
declare const FLARE_VERSION = "0.4.1";
|
|
511
513
|
|
|
512
514
|
export { type BelongsToRelation, type Country, CreateInput, FLARE_VERSION, type Fake, Field, type FormValues, type FormatOptions, type HasManyRelation, type InsertMany, type MailMessage, type MailResult, type Mailer, type MailerOptions, PBKDF2_ITERATIONS, type ReadUrlOptions, RelationError, type RelationGraph, Resource, type ResourceRelations, type ResourceValidators, SNIFF_BYTES, SUBSCRIPTION_STATUSES, type Seed, type SeedContext, type Storage, type StorageBucket, type StorageOptions, StoredField, type SubscriptionLike, type SubscriptionState, type SubscriptionStatus, type Tone, type TransactionalEmail, UpdateInput, type UploadUrlOptions, camelCase, checkoutPaid, countries, countryName, createFake, createMailer, createObjectKey, createStorage, createValidators, currentPeriodEnd, defineSeed, fake, fieldSchema, fileKeyPrefix, flagEmoji, formValuesToInput, formatPhone, formatValue, grantsAccess, hashPassword, humanize, importSigningKey, initialFormValues, isColor, isCountryCode, isDomain, isLiveSubscription, isPhoneNumber, isSlug, issuesByField, kebabCase, matchesContentType, normalizeDomain, optionLabel, parseMultiValue, pascalCase, pluralize, randomLetters, relationGraph, renderTransactionalEmail, shouldApplySubscription, signToken, slugify, snakeCase, sniffMatches, splitPhone, statusTone, subscriptionPriceId, subscriptionState, toE164, toSubscriptionStatus, verifyPassword, verifyToken, words };
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
allowedActions,
|
|
9
9
|
camelCase,
|
|
10
10
|
can,
|
|
11
|
+
clientResource,
|
|
11
12
|
columnName,
|
|
12
13
|
countries,
|
|
13
14
|
countryName,
|
|
@@ -38,7 +39,7 @@ import {
|
|
|
38
39
|
storedFields,
|
|
39
40
|
toE164,
|
|
40
41
|
words
|
|
41
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-VLHPSNQN.js";
|
|
42
43
|
|
|
43
44
|
// src/password.ts
|
|
44
45
|
var ALGORITHM = "pbkdf2-sha256";
|
|
@@ -241,6 +242,8 @@ var EMPTY_ZIP = [80, 75, 5, 6];
|
|
|
241
242
|
var OLE2 = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
242
243
|
var EBML = [26, 69, 223, 163];
|
|
243
244
|
var isZip = (head) => startsWith(head, ZIP) || startsWith(head, EMPTY_ZIP);
|
|
245
|
+
var isGzip = (head) => startsWith(head, [31, 139]);
|
|
246
|
+
var isRar = (head) => startsWith(head, ascii("Rar!"));
|
|
244
247
|
var EXACT = {
|
|
245
248
|
"image/png": (head) => startsWith(head, [137, 80, 78, 71, 13, 10, 26, 10]),
|
|
246
249
|
"image/jpeg": (head) => startsWith(head, [255, 216, 255]),
|
|
@@ -249,6 +252,13 @@ var EXACT = {
|
|
|
249
252
|
"image/avif": (head) => ["avif", "avis", "mif1", "msf1"].includes(isoBrand(head) ?? ""),
|
|
250
253
|
"application/pdf": (head) => startsWith(head, ascii("%PDF-")),
|
|
251
254
|
"application/zip": isZip,
|
|
255
|
+
"application/x-zip-compressed": isZip,
|
|
256
|
+
"application/x-zip": isZip,
|
|
257
|
+
"application/gzip": isGzip,
|
|
258
|
+
"application/x-gzip": isGzip,
|
|
259
|
+
"application/x-7z-compressed": (head) => startsWith(head, [55, 122, 188, 175, 39, 28]),
|
|
260
|
+
"application/vnd.rar": isRar,
|
|
261
|
+
"application/x-rar-compressed": isRar,
|
|
252
262
|
// Office Open XML and OpenDocument files are zip archives.
|
|
253
263
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": isZip,
|
|
254
264
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": isZip,
|
|
@@ -281,6 +291,7 @@ function matchesContentType(contentType, allowed) {
|
|
|
281
291
|
if (!/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/.test(type)) return false;
|
|
282
292
|
return allowed.some((pattern) => {
|
|
283
293
|
const p = pattern.trim().toLowerCase();
|
|
294
|
+
if (p === "*/*") return true;
|
|
284
295
|
return p.endsWith("/*") ? type.startsWith(p.slice(0, -1)) : type === p;
|
|
285
296
|
});
|
|
286
297
|
}
|
|
@@ -873,6 +884,26 @@ var WORDS = [
|
|
|
873
884
|
"variance",
|
|
874
885
|
"workflow"
|
|
875
886
|
];
|
|
887
|
+
var PRODUCT_QUALITIES = ["Oak", "Walnut", "Brushed steel", "Canvas", "Merino", "Recycled", "Matte black", "Linen", "Copper", "Frosted glass"];
|
|
888
|
+
var PRODUCT_THINGS = [
|
|
889
|
+
"desk lamp",
|
|
890
|
+
"office chair",
|
|
891
|
+
"notebook",
|
|
892
|
+
"water bottle",
|
|
893
|
+
"backpack",
|
|
894
|
+
"keyboard",
|
|
895
|
+
"mouse mat",
|
|
896
|
+
"coffee grinder",
|
|
897
|
+
"travel mug",
|
|
898
|
+
"wall clock",
|
|
899
|
+
"storage box",
|
|
900
|
+
"cable tidy",
|
|
901
|
+
"monitor stand",
|
|
902
|
+
"planter",
|
|
903
|
+
"door mat",
|
|
904
|
+
"tote bag"
|
|
905
|
+
];
|
|
906
|
+
var DIGITAL_THINGS = ["starter kit", "source code licence", "icon pack", "template bundle", "course", "font family", "preset pack", "e-book"];
|
|
876
907
|
var DOMAINS = ["example.com", "example.org", "example.net", "mail.example.com"];
|
|
877
908
|
var TLDS = ["com", "io", "co", "org", "net"];
|
|
878
909
|
var COLORS = ["#f2541d", "#2563eb", "#16a34a", "#d97706", "#7c3aed", "#0891b2", "#db2777", "#4b5563"];
|
|
@@ -924,6 +955,7 @@ function createFake(seed = 20260101) {
|
|
|
924
955
|
return `${slugify2(local).replace(/-/g, ".")}${step()}@${pick(DOMAINS)}`;
|
|
925
956
|
},
|
|
926
957
|
company: () => `${pick(COMPANY_HEADS)} ${pick(COMPANY_TAILS)}`,
|
|
958
|
+
product: (digital = false) => digital ? `${pick(PRODUCT_QUALITIES)} ${pick(DIGITAL_THINGS)}` : `${pick(PRODUCT_QUALITIES)} ${pick(PRODUCT_THINGS)}`,
|
|
927
959
|
jobTitle: () => `${pick(["Head of", "Senior", "Lead", "Regional", "Junior"])} ${pick(["Operations", "Sales", "Support", "Engineering", "Finance", "Logistics"])}`,
|
|
928
960
|
city: () => pick(CITIES),
|
|
929
961
|
country: () => pick(COUNTRIES)[0],
|
|
@@ -962,7 +994,7 @@ function createFake(seed = 20260101) {
|
|
|
962
994
|
var fake = createFake();
|
|
963
995
|
|
|
964
996
|
// src/index.ts
|
|
965
|
-
var FLARE_VERSION = "0.4.
|
|
997
|
+
var FLARE_VERSION = "0.4.1";
|
|
966
998
|
export {
|
|
967
999
|
DEFAULT_FILE_MAX_BYTES,
|
|
968
1000
|
FILE_CATEGORIES,
|
|
@@ -979,6 +1011,7 @@ export {
|
|
|
979
1011
|
camelCase,
|
|
980
1012
|
can,
|
|
981
1013
|
checkoutPaid,
|
|
1014
|
+
clientResource,
|
|
982
1015
|
columnName,
|
|
983
1016
|
countries,
|
|
984
1017
|
countryName,
|
|
@@ -116,6 +116,7 @@ type Field = StringField | TextField | IntField | FloatField | BooleanField | Da
|
|
|
116
116
|
type StoredField = Exclude<Field, HasManyField>;
|
|
117
117
|
/** File categories usable in `file:[...]` and the MIME types each allows. */
|
|
118
118
|
declare const FILE_CATEGORIES: {
|
|
119
|
+
readonly any: readonly ["*/*"];
|
|
119
120
|
readonly image: readonly ["image/png", "image/jpeg", "image/gif", "image/webp", "image/avif"];
|
|
120
121
|
readonly pdf: readonly ["application/pdf"];
|
|
121
122
|
readonly video: readonly ["video/*"];
|
|
@@ -124,7 +125,7 @@ declare const FILE_CATEGORIES: {
|
|
|
124
125
|
readonly csv: readonly ["text/csv"];
|
|
125
126
|
readonly document: readonly ["application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.oasis.opendocument.text"];
|
|
126
127
|
readonly spreadsheet: readonly ["application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.oasis.opendocument.spreadsheet"];
|
|
127
|
-
readonly archive: readonly ["application/zip"];
|
|
128
|
+
readonly archive: readonly ["application/zip", "application/x-zip-compressed", "application/x-zip", "application/gzip", "application/x-gzip", "application/x-7z-compressed", "application/vnd.rar", "application/x-rar-compressed"];
|
|
128
129
|
};
|
|
129
130
|
type FileCategory = keyof typeof FILE_CATEGORIES;
|
|
130
131
|
declare function mimeTypesFor(accept: readonly FileCategory[]): string[];
|
|
@@ -261,7 +262,11 @@ interface HookContext {
|
|
|
261
262
|
} | null;
|
|
262
263
|
}
|
|
263
264
|
interface ResourceHooks {
|
|
264
|
-
/**
|
|
265
|
+
/**
|
|
266
|
+
* Change or check the input before it's written. Return the input (changed or not), or
|
|
267
|
+
* throw to refuse. It runs *before* validation, so it can fill in a field the caller
|
|
268
|
+
* couldn't supply — an order number, a slug — and what it returns is then validated.
|
|
269
|
+
*/
|
|
265
270
|
beforeCreate?: (input: Record<string, unknown>, context: HookContext) => Promise<Record<string, unknown>> | Record<string, unknown>;
|
|
266
271
|
afterCreate?: (record: Record<string, unknown>, context: HookContext) => Promise<void> | void;
|
|
267
272
|
beforeUpdate?: (input: Record<string, unknown>, context: HookContext & {
|
|
@@ -307,6 +312,22 @@ interface Resource<Fields extends Record<string, Field> = Record<string, Field>>
|
|
|
307
312
|
record: RecordOf<Fields>;
|
|
308
313
|
};
|
|
309
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* A resource as a client component can receive it: everything that describes the fields,
|
|
317
|
+
* without the code.
|
|
318
|
+
*
|
|
319
|
+
* `hooks` and `computed` hold functions, and a function can't cross from the server to
|
|
320
|
+
* the browser — React refuses a prop it wasn't told to expose, and the page fails to
|
|
321
|
+
* render. So a server component hands a client one `clientResource(resource)` instead.
|
|
322
|
+
* The `never` types below turn passing the whole resource into a type error, which is
|
|
323
|
+
* cheaper to find than the runtime one.
|
|
324
|
+
*/
|
|
325
|
+
type ClientResource<Fields extends Record<string, Field> = Record<string, Field>> = Omit<Resource<Fields>, "hooks" | "computed" | "$types"> & {
|
|
326
|
+
hooks?: never;
|
|
327
|
+
computed?: never;
|
|
328
|
+
};
|
|
329
|
+
/** The parts of a resource that are safe to send to the browser. */
|
|
330
|
+
declare function clientResource<Fields extends Record<string, Field>>(resource: Resource<Fields>): ClientResource<Fields>;
|
|
310
331
|
declare class ResourceDefinitionError extends Error {
|
|
311
332
|
constructor(resource: string, message: string);
|
|
312
333
|
}
|
|
@@ -314,7 +335,7 @@ declare class ResourceDefinitionError extends Error {
|
|
|
314
335
|
declare function defaultFieldLabel(key: string, field: Field): string;
|
|
315
336
|
declare function defineResource<const Fields extends Record<string, Field>>(config: ResourceConfig<Fields>): Resource<Fields>;
|
|
316
337
|
/** Stored (column-backed) fields of a resource, in declaration order. */
|
|
317
|
-
declare function storedFields(resource: Resource): [string, StoredField & {
|
|
338
|
+
declare function storedFields(resource: Pick<Resource, "fields">): [string, StoredField & {
|
|
318
339
|
label: string;
|
|
319
340
|
}][];
|
|
320
341
|
/** Database column name for a field key ("companyId" → "company_id"). */
|
|
@@ -356,4 +377,4 @@ declare function can(policy: Policy | undefined | null, role: string | null | un
|
|
|
356
377
|
/** Actions `role` may perform, for hiding UI it can't use. */
|
|
357
378
|
declare function allowedActions(policy: Policy | undefined | null, role: string | null | undefined): Record<Exclude<PolicyAction, "list">, boolean>;
|
|
358
379
|
|
|
359
|
-
export {
|
|
380
|
+
export { defineResource as A, type BelongsToField as B, type CreateInput as C, DEFAULT_FILE_MAX_BYTES as D, type EnumField as E, type Field as F, field as G, type HasManyField as H, type IntField as I, fileMaxBytes as J, mimeTypesFor as K, storedFields as L, type MultiSelectField as M, type Policy as P, type Resource as R, type StoredField as S, type TextField as T, type UpdateInput as U, type BooleanField as a, type ClientResource as b, type DateField as c, type DateTimeField as d, FILE_CATEGORIES as e, type FieldKind as f, type FieldValue as g, type FileCategory as h, type FileField as i, type FloatField as j, type PolicyAction as k, type PolicyConfig as l, PolicyError as m, RESERVED_FIELD_NAMES as n, type RecordOf as o, type ResourceConfig as p, ResourceDefinitionError as q, STRING_FORMATS as r, type StringField as s, type StringFormat as t, allowedActions as u, can as v, clientResource as w, columnName as x, defaultFieldLabel as y, definePolicy as z };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as drizzle_orm_sqlite_core from 'drizzle-orm/sqlite-core';
|
|
2
2
|
import { BaseSQLiteDatabase, SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
3
|
-
import { S as StoredField, R as Resource, F as Field, P as Policy } from './policy-
|
|
3
|
+
import { S as StoredField, R as Resource, F as Field, P as Policy } from './policy-DaKcWZ5g.js';
|
|
4
4
|
|
|
5
5
|
interface ListQuery {
|
|
6
6
|
page: number;
|
|
@@ -130,9 +130,9 @@ declare function createResourceStore(options: ResourceStoreOptions): {
|
|
|
130
130
|
get(id: string): Promise<Result<Record<string, unknown>>>;
|
|
131
131
|
/** Title-field values for a set of ids (for showing relations). Missing ids are omitted. */
|
|
132
132
|
titles(ids: string[]): Promise<Record<string, string>>;
|
|
133
|
-
create(
|
|
133
|
+
create(raw: unknown): Promise<Result<Record<string, unknown>>>;
|
|
134
134
|
/** Partial update (PATCH semantics). */
|
|
135
|
-
update(id: string,
|
|
135
|
+
update(id: string, raw: unknown): Promise<Result<Record<string, unknown>>>;
|
|
136
136
|
/** Full replacement (PUT semantics): omitted fields go back to their default, or null when optional. */
|
|
137
137
|
replace(id: string, input: unknown): Promise<Result<Record<string, unknown>>>;
|
|
138
138
|
delete(id: string): Promise<Result<{
|
|
@@ -182,8 +182,8 @@ declare function createResourceHandlers(options: ResourceHandlerOptions): {
|
|
|
182
182
|
list(params: URLSearchParams): Promise<Result<ListResult>>;
|
|
183
183
|
get(id: string): Promise<Result<Record<string, unknown>>>;
|
|
184
184
|
titles(ids: string[]): Promise<Record<string, string>>;
|
|
185
|
-
create(
|
|
186
|
-
update(id: string,
|
|
185
|
+
create(raw: unknown): Promise<Result<Record<string, unknown>>>;
|
|
186
|
+
update(id: string, raw: unknown): Promise<Result<Record<string, unknown>>>;
|
|
187
187
|
replace(id: string, input: unknown): Promise<Result<Record<string, unknown>>>;
|
|
188
188
|
delete(id: string): Promise<Result<{
|
|
189
189
|
id: string;
|
package/dist/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
can,
|
|
3
3
|
createValidators,
|
|
4
4
|
storedFields
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-VLHPSNQN.js";
|
|
6
6
|
|
|
7
7
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudfl_b372b29e6d5511672c7f9f9d2ac40572/node_modules/drizzle-orm/entity.js
|
|
8
8
|
var entityKind = /* @__PURE__ */ Symbol.for("drizzle:entityKind");
|
|
@@ -1440,29 +1440,31 @@ function createResourceStore(options) {
|
|
|
1440
1440
|
const rows = await getDb().select({ id: idColumn, title: titleColumn }).from(table).where(inArray(idColumn, unique));
|
|
1441
1441
|
return Object.fromEntries(rows.map((row) => [row.id, row.title == null ? row.id : String(row.title)]));
|
|
1442
1442
|
},
|
|
1443
|
-
create(
|
|
1444
|
-
const result = validators.create.safeParse(input);
|
|
1445
|
-
if (!result.success) return Promise.resolve(invalid(result.error.issues));
|
|
1443
|
+
create(raw) {
|
|
1446
1444
|
return writing("create", async () => {
|
|
1447
1445
|
const context = await hookContext();
|
|
1448
|
-
const
|
|
1446
|
+
const supplied = hooks.beforeCreate ? await hooks.beforeCreate({ ...raw ?? {} }, context) : raw;
|
|
1447
|
+
const result = validators.create.safeParse(supplied);
|
|
1448
|
+
if (!result.success) return invalid(result.error.issues);
|
|
1449
|
+
const input = result.data;
|
|
1449
1450
|
const now = /* @__PURE__ */ new Date();
|
|
1450
|
-
const [record] = await getDb().insert(table).values({ ...
|
|
1451
|
+
const [record] = await getDb().insert(table).values({ ...input, id: crypto.randomUUID(), createdAt: now, updatedAt: now }).returning();
|
|
1451
1452
|
const saved = withComputed(record);
|
|
1452
1453
|
await hooks.afterCreate?.(saved, context);
|
|
1453
1454
|
return { ok: true, data: saved };
|
|
1454
1455
|
});
|
|
1455
1456
|
},
|
|
1456
1457
|
/** Partial update (PATCH semantics). */
|
|
1457
|
-
update(id,
|
|
1458
|
-
const result = validators.update.safeParse(input);
|
|
1459
|
-
if (!result.success) return Promise.resolve(invalid(result.error.issues));
|
|
1458
|
+
update(id, raw) {
|
|
1460
1459
|
return writing("update", async () => {
|
|
1461
1460
|
const context = await hookContext();
|
|
1462
1461
|
const [existing] = hooks.beforeUpdate || hooks.afterUpdate ? await getDb().select().from(table).where(byId(id)).limit(1) : [];
|
|
1463
1462
|
const current = existing ?? null;
|
|
1464
|
-
const
|
|
1465
|
-
const
|
|
1463
|
+
const supplied = hooks.beforeUpdate ? await hooks.beforeUpdate({ ...raw ?? {} }, { ...context, id, current }) : raw;
|
|
1464
|
+
const result = validators.update.safeParse(supplied);
|
|
1465
|
+
if (!result.success) return invalid(result.error.issues);
|
|
1466
|
+
const input = result.data;
|
|
1467
|
+
const [record] = await getDb().update(table).set({ ...input, updatedAt: /* @__PURE__ */ new Date() }).where(byId(id)).returning();
|
|
1466
1468
|
if (!record) return notFound();
|
|
1467
1469
|
const saved = withComputed(record);
|
|
1468
1470
|
await hooks.afterUpdate?.(saved, { ...context, previous: current });
|