@flaredev/core 0.1.1 → 0.2.0
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/chunk-434JXSDV.js +410 -0
- package/dist/index.d.ts +39 -39
- package/dist/index.js +63 -81
- package/dist/{define-D5CtCkOm.d.ts → policy-BqRg9RVM.d.ts} +80 -4
- package/dist/server.d.ts +29 -2
- package/dist/server.js +1448 -3
- package/package.json +2 -13
- package/dist/chunk-DARDHZPX.js +0 -192
package/dist/index.js
CHANGED
|
@@ -1,21 +1,44 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_FILE_MAX_BYTES,
|
|
3
|
+
FILE_CATEGORIES,
|
|
4
|
+
PolicyError,
|
|
2
5
|
RESERVED_FIELD_NAMES,
|
|
3
6
|
ResourceDefinitionError,
|
|
7
|
+
STRING_FORMATS,
|
|
8
|
+
allowedActions,
|
|
4
9
|
camelCase,
|
|
10
|
+
can,
|
|
5
11
|
columnName,
|
|
12
|
+
countries,
|
|
13
|
+
countryName,
|
|
6
14
|
createValidators,
|
|
7
15
|
defaultFieldLabel,
|
|
16
|
+
definePolicy,
|
|
8
17
|
defineResource,
|
|
18
|
+
field,
|
|
9
19
|
fieldSchema,
|
|
10
20
|
fileKeyPrefix,
|
|
21
|
+
fileMaxBytes,
|
|
22
|
+
flagEmoji,
|
|
23
|
+
formatPhone,
|
|
11
24
|
humanize,
|
|
25
|
+
isColor,
|
|
26
|
+
isCountryCode,
|
|
27
|
+
isDomain,
|
|
28
|
+
isPhoneNumber,
|
|
29
|
+
isSlug,
|
|
12
30
|
kebabCase,
|
|
31
|
+
mimeTypesFor,
|
|
32
|
+
normalizeDomain,
|
|
13
33
|
pascalCase,
|
|
14
34
|
pluralize,
|
|
35
|
+
slugify,
|
|
15
36
|
snakeCase,
|
|
37
|
+
splitPhone,
|
|
16
38
|
storedFields,
|
|
39
|
+
toE164,
|
|
17
40
|
words
|
|
18
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-434JXSDV.js";
|
|
19
42
|
|
|
20
43
|
// src/password.ts
|
|
21
44
|
var ALGORITHM = "pbkdf2-sha256";
|
|
@@ -466,48 +489,6 @@ function randomLetters(length = 8) {
|
|
|
466
489
|
return Array.from(bytes, (byte) => String.fromCharCode(97 + byte % 26)).join("");
|
|
467
490
|
}
|
|
468
491
|
|
|
469
|
-
// src/resource/fields.ts
|
|
470
|
-
var FILE_CATEGORIES = {
|
|
471
|
-
image: ["image/png", "image/jpeg", "image/gif", "image/webp", "image/avif"],
|
|
472
|
-
pdf: ["application/pdf"],
|
|
473
|
-
video: ["video/*"],
|
|
474
|
-
audio: ["audio/*"],
|
|
475
|
-
text: ["text/plain"],
|
|
476
|
-
csv: ["text/csv"],
|
|
477
|
-
document: [
|
|
478
|
-
"application/msword",
|
|
479
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
480
|
-
"application/vnd.oasis.opendocument.text"
|
|
481
|
-
],
|
|
482
|
-
spreadsheet: [
|
|
483
|
-
"application/vnd.ms-excel",
|
|
484
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
485
|
-
"application/vnd.oasis.opendocument.spreadsheet"
|
|
486
|
-
],
|
|
487
|
-
archive: ["application/zip"]
|
|
488
|
-
};
|
|
489
|
-
function mimeTypesFor(accept) {
|
|
490
|
-
return [...new Set(accept.flatMap((category) => FILE_CATEGORIES[category]))];
|
|
491
|
-
}
|
|
492
|
-
var DEFAULT_FILE_MAX_BYTES = 10 * 1024 * 1024;
|
|
493
|
-
var fileMaxBytes = (field2) => field2.maxBytes ?? DEFAULT_FILE_MAX_BYTES;
|
|
494
|
-
function build(kind, options, extra = {}) {
|
|
495
|
-
return { kind, ...extra, ...options, required: options?.required !== false };
|
|
496
|
-
}
|
|
497
|
-
var field = {
|
|
498
|
-
string: (options) => build("string", options),
|
|
499
|
-
text: (options) => build("text", options, { list: false, searchable: false }),
|
|
500
|
-
int: (options) => build("int", options),
|
|
501
|
-
float: (options) => build("float", options),
|
|
502
|
-
boolean: (options) => build("boolean", options),
|
|
503
|
-
date: (options) => build("date", options),
|
|
504
|
-
datetime: (options) => build("datetime", options),
|
|
505
|
-
enum: (values, options) => build("enum", options, { options: values }),
|
|
506
|
-
file: (accept, options) => build("file", options, { accept, list: false, sortable: false }),
|
|
507
|
-
belongsTo: (target, options) => build("belongsTo", options, { target }),
|
|
508
|
-
hasMany: (target, options) => ({ kind: "hasMany", target, ...options, required: false })
|
|
509
|
-
};
|
|
510
|
-
|
|
511
492
|
// src/resource/relations.ts
|
|
512
493
|
var RelationError = class extends Error {
|
|
513
494
|
constructor(message) {
|
|
@@ -585,7 +566,7 @@ function statusTone(value) {
|
|
|
585
566
|
return TONES.find(([, pattern]) => pattern.test(normalized))?.[0] ?? "neutral";
|
|
586
567
|
}
|
|
587
568
|
function optionLabel(def, value) {
|
|
588
|
-
if (def.kind === "enum" && def.optionLabels?.[value]) return def.optionLabels[value];
|
|
569
|
+
if ((def.kind === "enum" || def.kind === "multiselect") && def.optionLabels?.[value]) return def.optionLabels[value];
|
|
589
570
|
const text2 = value.replace(/[_-]+/g, " ").trim();
|
|
590
571
|
return text2 ? text2[0].toUpperCase() + text2.slice(1) : value;
|
|
591
572
|
}
|
|
@@ -610,6 +591,12 @@ function formatValue(def, value, options = {}) {
|
|
|
610
591
|
}
|
|
611
592
|
case "enum":
|
|
612
593
|
return optionLabel(def, String(value));
|
|
594
|
+
case "multiselect":
|
|
595
|
+
return Array.isArray(value) && value.length ? value.map((item) => optionLabel(def, String(item))).join(", ") : "\u2014";
|
|
596
|
+
case "string":
|
|
597
|
+
if (def.format === "tel") return formatPhone(String(value));
|
|
598
|
+
if (def.format === "country") return `${flagEmoji(String(value))} ${countryName(String(value), locale.split("-")[0])}`.trim();
|
|
599
|
+
return String(value);
|
|
613
600
|
case "file":
|
|
614
601
|
return String(value).split("/").pop().replace(/^[0-9a-f-]{36}-/, "");
|
|
615
602
|
default:
|
|
@@ -623,6 +610,7 @@ function initialFormValues(resource, record) {
|
|
|
623
610
|
for (const [key, def] of storedFields(resource)) {
|
|
624
611
|
const source = record ? record[key] : "default" in def ? def.default : void 0;
|
|
625
612
|
if (def.kind === "boolean") values[key] = source === true || source === 1;
|
|
613
|
+
else if (def.kind === "multiselect") values[key] = JSON.stringify(Array.isArray(source) ? source : []);
|
|
626
614
|
else if (source === null || source === void 0) values[key] = "";
|
|
627
615
|
else if (source instanceof Date) values[key] = source.toISOString();
|
|
628
616
|
else values[key] = String(source);
|
|
@@ -637,6 +625,12 @@ function formValuesToInput(resource, values, mode) {
|
|
|
637
625
|
input[key] = raw === true;
|
|
638
626
|
continue;
|
|
639
627
|
}
|
|
628
|
+
if (def.kind === "multiselect") {
|
|
629
|
+
const picked = parseMultiValue(raw);
|
|
630
|
+
if (picked.length === 0 && mode === "create" && "default" in def && def.default !== void 0) continue;
|
|
631
|
+
input[key] = picked.length === 0 && !def.required ? null : picked;
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
640
634
|
const text2 = typeof raw === "string" ? raw : "";
|
|
641
635
|
const empty = text2.trim() === "";
|
|
642
636
|
if (empty) {
|
|
@@ -655,6 +649,15 @@ function formValuesToInput(resource, values, mode) {
|
|
|
655
649
|
}
|
|
656
650
|
return input;
|
|
657
651
|
}
|
|
652
|
+
function parseMultiValue(raw) {
|
|
653
|
+
if (typeof raw !== "string" || raw === "") return [];
|
|
654
|
+
try {
|
|
655
|
+
const parsed = JSON.parse(raw);
|
|
656
|
+
return Array.isArray(parsed) ? parsed.filter((value) => typeof value === "string") : [];
|
|
657
|
+
} catch {
|
|
658
|
+
return [];
|
|
659
|
+
}
|
|
660
|
+
}
|
|
658
661
|
function issuesByField(issues) {
|
|
659
662
|
const errors = {};
|
|
660
663
|
for (const issue of issues) {
|
|
@@ -665,49 +668,13 @@ function issuesByField(issues) {
|
|
|
665
668
|
return errors;
|
|
666
669
|
}
|
|
667
670
|
|
|
668
|
-
// src/resource/policy.ts
|
|
669
|
-
var ROLE = /^(\*|[a-z][a-z0-9_-]{0,31})$/;
|
|
670
|
-
var PolicyError = class extends Error {
|
|
671
|
-
constructor(message) {
|
|
672
|
-
super(message);
|
|
673
|
-
this.name = "PolicyError";
|
|
674
|
-
}
|
|
675
|
-
};
|
|
676
|
-
function definePolicy(config) {
|
|
677
|
-
if (!/^[A-Z][A-Za-z0-9]*$/.test(config.resource)) {
|
|
678
|
-
throw new PolicyError(`Policy resource "${config.resource}" must be a PascalCase resource name.`);
|
|
679
|
-
}
|
|
680
|
-
const clean = (action) => {
|
|
681
|
-
const roles = config[action] ?? [];
|
|
682
|
-
for (const role of roles) {
|
|
683
|
-
if (!ROLE.test(role)) throw new PolicyError(`Policy ${config.resource}.${action}: invalid role "${role}" (lowercase letters, digits, _ or -, or "*").`);
|
|
684
|
-
}
|
|
685
|
-
return [...new Set(roles)];
|
|
686
|
-
};
|
|
687
|
-
return { resource: config.resource, read: clean("read"), create: clean("create"), update: clean("update"), delete: clean("delete") };
|
|
688
|
-
}
|
|
689
|
-
function can(policy, role, action) {
|
|
690
|
-
if (!policy) return true;
|
|
691
|
-
const allowed = policy[action === "list" ? "read" : action];
|
|
692
|
-
if (allowed.includes("*")) return Boolean(role);
|
|
693
|
-
return Boolean(role) && allowed.includes(role);
|
|
694
|
-
}
|
|
695
|
-
function allowedActions(policy, role) {
|
|
696
|
-
return {
|
|
697
|
-
read: can(policy, role, "read"),
|
|
698
|
-
create: can(policy, role, "create"),
|
|
699
|
-
update: can(policy, role, "update"),
|
|
700
|
-
delete: can(policy, role, "delete")
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
|
|
704
671
|
// src/seed.ts
|
|
705
672
|
function defineSeed(seed) {
|
|
706
673
|
return seed;
|
|
707
674
|
}
|
|
708
675
|
|
|
709
676
|
// src/index.ts
|
|
710
|
-
var FLARE_VERSION = "0.
|
|
677
|
+
var FLARE_VERSION = "0.2.0";
|
|
711
678
|
export {
|
|
712
679
|
DEFAULT_FILE_MAX_BYTES,
|
|
713
680
|
FILE_CATEGORIES,
|
|
@@ -718,12 +685,15 @@ export {
|
|
|
718
685
|
RelationError,
|
|
719
686
|
ResourceDefinitionError,
|
|
720
687
|
SNIFF_BYTES,
|
|
688
|
+
STRING_FORMATS,
|
|
721
689
|
SUBSCRIPTION_STATUSES,
|
|
722
690
|
allowedActions,
|
|
723
691
|
camelCase,
|
|
724
692
|
can,
|
|
725
693
|
checkoutPaid,
|
|
726
694
|
columnName,
|
|
695
|
+
countries,
|
|
696
|
+
countryName,
|
|
727
697
|
createMailer,
|
|
728
698
|
createObjectKey,
|
|
729
699
|
createStorage,
|
|
@@ -737,19 +707,28 @@ export {
|
|
|
737
707
|
fieldSchema,
|
|
738
708
|
fileKeyPrefix,
|
|
739
709
|
fileMaxBytes,
|
|
710
|
+
flagEmoji,
|
|
740
711
|
formValuesToInput,
|
|
712
|
+
formatPhone,
|
|
741
713
|
formatValue,
|
|
742
714
|
grantsAccess,
|
|
743
715
|
hashPassword,
|
|
744
716
|
humanize,
|
|
745
717
|
importSigningKey,
|
|
746
718
|
initialFormValues,
|
|
719
|
+
isColor,
|
|
720
|
+
isCountryCode,
|
|
721
|
+
isDomain,
|
|
747
722
|
isLiveSubscription,
|
|
723
|
+
isPhoneNumber,
|
|
724
|
+
isSlug,
|
|
748
725
|
issuesByField,
|
|
749
726
|
kebabCase,
|
|
750
727
|
matchesContentType,
|
|
751
728
|
mimeTypesFor,
|
|
729
|
+
normalizeDomain,
|
|
752
730
|
optionLabel,
|
|
731
|
+
parseMultiValue,
|
|
753
732
|
pascalCase,
|
|
754
733
|
pluralize,
|
|
755
734
|
randomLetters,
|
|
@@ -757,12 +736,15 @@ export {
|
|
|
757
736
|
renderTransactionalEmail,
|
|
758
737
|
shouldApplySubscription,
|
|
759
738
|
signToken,
|
|
739
|
+
slugify,
|
|
760
740
|
snakeCase,
|
|
761
741
|
sniffMatches,
|
|
742
|
+
splitPhone,
|
|
762
743
|
statusTone,
|
|
763
744
|
storedFields,
|
|
764
745
|
subscriptionPriceId,
|
|
765
746
|
subscriptionState,
|
|
747
|
+
toE164,
|
|
766
748
|
toSubscriptionStatus,
|
|
767
749
|
verifyPassword,
|
|
768
750
|
verifyToken,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* rule in a descriptor takes effect without re-running the generator. Only
|
|
7
7
|
* storage-level changes (kind, nullability, uniqueness, relations) need a migration.
|
|
8
8
|
*/
|
|
9
|
-
type FieldKind = "string" | "text" | "int" | "float" | "boolean" | "date" | "datetime" | "enum" | "file" | "belongsTo" | "hasMany";
|
|
9
|
+
type FieldKind = "string" | "text" | "int" | "float" | "boolean" | "date" | "datetime" | "enum" | "multiselect" | "file" | "belongsTo" | "hasMany";
|
|
10
10
|
/** Options shared by every stored (non-hasMany) field. */
|
|
11
11
|
interface CommonOptions<T> {
|
|
12
12
|
label?: string;
|
|
@@ -24,10 +24,23 @@ interface CommonOptions<T> {
|
|
|
24
24
|
/** Included in free-text search. Default true for string fields. */
|
|
25
25
|
searchable?: boolean;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* What a string holds. Each format is still a text column; the format decides
|
|
29
|
+
* validation, the admin input and how the value is shown.
|
|
30
|
+
*
|
|
31
|
+
* - email, url: validated addresses (url: http or https)
|
|
32
|
+
* - tel: an international number, stored E.164 (`+256772123456`), entered with a country picker
|
|
33
|
+
* - domain: a hostname such as `example.com`, stored lowercase
|
|
34
|
+
* - country: an ISO 3166-1 alpha-2 code (`UG`), chosen from a searchable list
|
|
35
|
+
* - color: a hex colour (`#f2541d`)
|
|
36
|
+
* - slug: lowercase words joined by hyphens (`my-first-post`)
|
|
37
|
+
*/
|
|
38
|
+
type StringFormat = "email" | "url" | "tel" | "domain" | "country" | "color" | "slug";
|
|
39
|
+
declare const STRING_FORMATS: readonly StringFormat[];
|
|
27
40
|
interface StringOptions extends CommonOptions<string> {
|
|
28
41
|
maxLength?: number;
|
|
29
42
|
minLength?: number;
|
|
30
|
-
format?:
|
|
43
|
+
format?: StringFormat;
|
|
31
44
|
pattern?: string;
|
|
32
45
|
}
|
|
33
46
|
interface TextOptions extends CommonOptions<string> {
|
|
@@ -43,6 +56,14 @@ type DateOptions = CommonOptions<string>;
|
|
|
43
56
|
interface EnumOptions<O extends string> extends CommonOptions<O> {
|
|
44
57
|
/** Display labels per option value. */
|
|
45
58
|
optionLabels?: Partial<Record<O, string>>;
|
|
59
|
+
/** Admin input: a dropdown (default) or radio buttons (best for a few options). */
|
|
60
|
+
widget?: "select" | "radio";
|
|
61
|
+
}
|
|
62
|
+
interface MultiSelectOptions<O extends string> extends Omit<CommonOptions<O[]>, "unique"> {
|
|
63
|
+
optionLabels?: Partial<Record<O, string>>;
|
|
64
|
+
/** Fewest / most options that can be picked. A required field needs at least one. */
|
|
65
|
+
minItems?: number;
|
|
66
|
+
maxItems?: number;
|
|
46
67
|
}
|
|
47
68
|
interface FileOptions extends CommonOptions<string> {
|
|
48
69
|
/** Maximum upload size in bytes. Default 10 MiB. */
|
|
@@ -75,6 +96,9 @@ type DateTimeField<R extends boolean = boolean> = Base<"datetime", R> & DateOpti
|
|
|
75
96
|
type EnumField<O extends string = string, R extends boolean = boolean> = Base<"enum", R> & EnumOptions<O> & {
|
|
76
97
|
options: readonly O[];
|
|
77
98
|
};
|
|
99
|
+
type MultiSelectField<O extends string = string, R extends boolean = boolean> = Base<"multiselect", R> & MultiSelectOptions<O> & {
|
|
100
|
+
options: readonly O[];
|
|
101
|
+
};
|
|
78
102
|
type FileField<R extends boolean = boolean> = Base<"file", R> & FileOptions & {
|
|
79
103
|
/** Accepted categories from the `file:[image,pdf]` grammar. */
|
|
80
104
|
accept: readonly FileCategory[];
|
|
@@ -88,7 +112,7 @@ type HasManyField = {
|
|
|
88
112
|
required: false;
|
|
89
113
|
target: string;
|
|
90
114
|
} & HasManyOptions;
|
|
91
|
-
type Field = StringField | TextField | IntField | FloatField | BooleanField | DateField | DateTimeField | EnumField | FileField | BelongsToField | HasManyField;
|
|
115
|
+
type Field = StringField | TextField | IntField | FloatField | BooleanField | DateField | DateTimeField | EnumField | MultiSelectField | FileField | BelongsToField | HasManyField;
|
|
92
116
|
type StoredField = Exclude<Field, HasManyField>;
|
|
93
117
|
/** File categories usable in `file:[...]` and the MIME types each allows. */
|
|
94
118
|
declare const FILE_CATEGORIES: {
|
|
@@ -119,6 +143,19 @@ declare const field: {
|
|
|
119
143
|
date: <const O extends DateOptions>(options?: O) => DateField<Req<O>> & O;
|
|
120
144
|
datetime: <const O extends DateOptions>(options?: O) => DateTimeField<Req<O>> & O;
|
|
121
145
|
enum: <const V extends readonly [string, ...string[]], const O extends EnumOptions<V[number]>>(values: V, options?: O) => EnumField<V[number], Req<O>> & O;
|
|
146
|
+
/** One of a list, picked from a dropdown. The same as `enum`. */
|
|
147
|
+
select: <const V extends readonly [string, ...string[]], const O extends EnumOptions<V[number]>>(values: V, options?: O) => EnumField<V[number], Req<O>> & O;
|
|
148
|
+
/** One of a list, picked with radio buttons. Stored like `enum`. */
|
|
149
|
+
radio: <const V extends readonly [string, ...string[]], const O extends EnumOptions<V[number]>>(values: V, options?: O) => EnumField<V[number], Req<O>> & O;
|
|
150
|
+
/** Any number of a list, picked with checkboxes. Stored as a JSON array. */
|
|
151
|
+
multiselect: <const V extends readonly [string, ...string[]], const O extends MultiSelectOptions<V[number]>>(values: V, options?: O) => MultiSelectField<V[number], Req<O>> & O;
|
|
152
|
+
email: <const O extends Omit<StringOptions, "format">>(options?: O) => StringField<Req<O>> & O;
|
|
153
|
+
url: <const O extends Omit<StringOptions, "format">>(options?: O) => StringField<Req<O>> & O;
|
|
154
|
+
tel: <const O extends Omit<StringOptions, "format">>(options?: O) => StringField<Req<O>> & O;
|
|
155
|
+
domain: <const O extends Omit<StringOptions, "format">>(options?: O) => StringField<Req<O>> & O;
|
|
156
|
+
country: <const O extends Omit<StringOptions, "format">>(options?: O) => StringField<Req<O>> & O;
|
|
157
|
+
color: <const O extends Omit<StringOptions, "format">>(options?: O) => StringField<Req<O>> & O;
|
|
158
|
+
slug: <const O extends Omit<StringOptions, "format">>(options?: O) => StringField<Req<O>> & O;
|
|
122
159
|
file: <const O extends FileOptions>(accept: readonly FileCategory[], options?: O) => FileField<Req<O>> & O;
|
|
123
160
|
belongsTo: <const O extends BelongsToOptions>(target: string, options?: O) => BelongsToField<Req<O>> & O;
|
|
124
161
|
hasMany: <const O extends HasManyOptions>(target: string, options?: O) => HasManyField & O;
|
|
@@ -131,6 +168,9 @@ type FieldValue<F> = F extends {
|
|
|
131
168
|
kind: "enum";
|
|
132
169
|
options: readonly (infer O)[];
|
|
133
170
|
} ? O : F extends {
|
|
171
|
+
kind: "multiselect";
|
|
172
|
+
options: readonly (infer M)[];
|
|
173
|
+
} ? M[] : F extends {
|
|
134
174
|
kind: "hasMany";
|
|
135
175
|
} ? never : string;
|
|
136
176
|
type StoredKeys<Fields> = {
|
|
@@ -229,4 +269,40 @@ declare function storedFields(resource: Resource): [string, StoredField & {
|
|
|
229
269
|
/** Database column name for a field key ("companyId" → "company_id"). */
|
|
230
270
|
declare const columnName: (key: string) => string;
|
|
231
271
|
|
|
232
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Resource-level policies: which roles may read, create, update, or delete a resource.
|
|
274
|
+
*
|
|
275
|
+
* Policies are plain data, like descriptors, so the API layer and the admin UI read the
|
|
276
|
+
* same rules at runtime. Field-level rules and per-record ownership are deliberately out
|
|
277
|
+
* of scope for v1.
|
|
278
|
+
*/
|
|
279
|
+
type PolicyAction = "list" | "read" | "create" | "update" | "delete";
|
|
280
|
+
/** Roles allowed per action; `"*"` means any signed-in user. */
|
|
281
|
+
interface Policy {
|
|
282
|
+
resource: string;
|
|
283
|
+
read: string[];
|
|
284
|
+
create: string[];
|
|
285
|
+
update: string[];
|
|
286
|
+
delete: string[];
|
|
287
|
+
}
|
|
288
|
+
interface PolicyConfig {
|
|
289
|
+
resource: string;
|
|
290
|
+
read?: string[];
|
|
291
|
+
create?: string[];
|
|
292
|
+
update?: string[];
|
|
293
|
+
delete?: string[];
|
|
294
|
+
}
|
|
295
|
+
declare class PolicyError extends Error {
|
|
296
|
+
constructor(message: string);
|
|
297
|
+
}
|
|
298
|
+
declare function definePolicy(config: PolicyConfig): Policy;
|
|
299
|
+
/**
|
|
300
|
+
* Whether `role` may perform `action`. Without a policy any signed-in user is allowed
|
|
301
|
+
* (callers must already have required a session); generate one with
|
|
302
|
+
* `flare gen policy <Resource> --roles …` to restrict a resource.
|
|
303
|
+
*/
|
|
304
|
+
declare function can(policy: Policy | undefined | null, role: string | null | undefined, action: PolicyAction): boolean;
|
|
305
|
+
/** Actions `role` may perform, for hiding UI it can't use. */
|
|
306
|
+
declare function allowedActions(policy: Policy | undefined | null, role: string | null | undefined): Record<Exclude<PolicyAction, "list">, boolean>;
|
|
307
|
+
|
|
308
|
+
export { fileMaxBytes as A, type BelongsToField as B, type CreateInput as C, DEFAULT_FILE_MAX_BYTES as D, type EnumField as E, type Field as F, mimeTypesFor as G, type HasManyField as H, type IntField as I, storedFields as J, 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 DateField as b, type DateTimeField as c, FILE_CATEGORIES as d, type FieldKind as e, type FieldValue as f, type FileCategory as g, type FileField as h, type FloatField as i, type PolicyAction as j, type PolicyConfig as k, PolicyError as l, RESERVED_FIELD_NAMES as m, type RecordOf as n, type ResourceConfig as o, ResourceDefinitionError as p, STRING_FORMATS as q, type StringField as r, type StringFormat as s, allowedActions as t, can as u, columnName as v, defaultFieldLabel as w, definePolicy as x, defineResource as y, field 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 } from './
|
|
3
|
+
import { S as StoredField, R as Resource, F as Field, P as Policy } from './policy-BqRg9RVM.js';
|
|
4
4
|
|
|
5
5
|
interface ListQuery {
|
|
6
6
|
page: number;
|
|
@@ -153,4 +153,31 @@ declare function createResourceHandlers(options: ResourceHandlerOptions): {
|
|
|
153
153
|
};
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
/**
|
|
157
|
+
* An OpenAPI 3.1 document for an app's generated REST API, built at request time
|
|
158
|
+
* from the same descriptors and policies the API itself runs on, so it can't drift.
|
|
159
|
+
*
|
|
160
|
+
* Operations are filtered by role: a signed-in user sees what their role may call
|
|
161
|
+
* (admins everything), a visitor only the sign-in endpoints. `visibility: "public"`
|
|
162
|
+
* lists every operation to everyone.
|
|
163
|
+
*/
|
|
164
|
+
|
|
165
|
+
type Schema = Record<string, unknown>;
|
|
166
|
+
interface OpenApiOptions {
|
|
167
|
+
resources: readonly Resource[];
|
|
168
|
+
policies?: Record<string, Policy | undefined>;
|
|
169
|
+
/** The viewer's role; null for a signed-out visitor. */
|
|
170
|
+
role: string | null;
|
|
171
|
+
/** "role" (default): only operations the viewer's role may call. "public": all of them. */
|
|
172
|
+
visibility?: "role" | "public";
|
|
173
|
+
title?: string;
|
|
174
|
+
version?: string;
|
|
175
|
+
description?: string;
|
|
176
|
+
/** Absolute origin the "Try it" requests go to, e.g. https://app.example.com. */
|
|
177
|
+
serverUrl?: string;
|
|
178
|
+
}
|
|
179
|
+
/** JSON Schema for one field value (never null; nullability is added by the caller). */
|
|
180
|
+
declare function fieldJsonSchema(def: StoredField): Schema;
|
|
181
|
+
declare function openApiDocument(options: OpenApiOptions): Schema;
|
|
182
|
+
|
|
183
|
+
export { type AnyDatabase, type Authorize, type AuthorizeContext, type ChangeEvent, type Failure, type FieldIssue, type ListQuery, type ListResult, type OpenApiOptions, type QueryIssue, type ResourceAction, type ResourceHandlerOptions, type ResourceStore, type ResourceStoreOptions, type Result, createResourceHandlers, createResourceStore, fieldJsonSchema, isFilterable, isSearchable, isSortable, openApiDocument, parseListQuery };
|