@flaredev/core 0.10.0 → 0.11.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.
|
@@ -266,6 +266,26 @@ function storedFields(resource) {
|
|
|
266
266
|
}
|
|
267
267
|
var columnName = (key) => snakeCase(key);
|
|
268
268
|
|
|
269
|
+
// src/resource/conditions.ts
|
|
270
|
+
function conditionHolds(condition, values) {
|
|
271
|
+
if (!condition) return true;
|
|
272
|
+
const actual = values[condition.field];
|
|
273
|
+
const text = actual === null || actual === void 0 ? "" : String(actual);
|
|
274
|
+
switch (condition.op) {
|
|
275
|
+
case "eq":
|
|
276
|
+
return text === String(condition.value);
|
|
277
|
+
case "ne":
|
|
278
|
+
return text !== String(condition.value);
|
|
279
|
+
case "in":
|
|
280
|
+
return (Array.isArray(condition.value) ? condition.value : [condition.value]).some((option) => String(option) === text);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function describeCondition(condition) {
|
|
284
|
+
const values = Array.isArray(condition.value) ? condition.value : [condition.value];
|
|
285
|
+
if (condition.op === "in") return `${condition.field} is one of ${values.join(", ")}`;
|
|
286
|
+
return `${condition.field} ${condition.op === "eq" ? "is" : "is not"} ${values[0]}`;
|
|
287
|
+
}
|
|
288
|
+
|
|
269
289
|
// src/resource/formats.ts
|
|
270
290
|
import {
|
|
271
291
|
getCountries,
|
|
@@ -480,15 +500,30 @@ function createValidators(resource) {
|
|
|
480
500
|
"This file wasn't uploaded for this field"
|
|
481
501
|
);
|
|
482
502
|
}
|
|
483
|
-
const
|
|
503
|
+
const conditional2 = def.showWhen !== void 0;
|
|
504
|
+
const value = def.required && !conditional2 ? base : base.nullable();
|
|
484
505
|
update[key] = value.optional();
|
|
485
506
|
const hasDefault = "default" in def && def.default !== void 0;
|
|
486
|
-
create[key] = def.required && !hasDefault ? value : value.optional();
|
|
507
|
+
create[key] = def.required && !conditional2 && !hasDefault ? value : value.optional();
|
|
487
508
|
}
|
|
509
|
+
const conditional = [...storedFields(resource)].flatMap(
|
|
510
|
+
([key, def]) => def.showWhen ? [{ key, condition: def.showWhen, required: def.required !== false }] : []
|
|
511
|
+
);
|
|
512
|
+
const checkConditions = (partial) => (values, ctx) => {
|
|
513
|
+
for (const { key, condition, required } of conditional) {
|
|
514
|
+
if (!required) continue;
|
|
515
|
+
if (partial && !(condition.field in values) && !(key in values)) continue;
|
|
516
|
+
if (!conditionHolds(condition, values)) continue;
|
|
517
|
+
const supplied = values[key];
|
|
518
|
+
if (supplied === void 0 || supplied === null || supplied === "") {
|
|
519
|
+
ctx.addIssue({ code: "custom", path: [key], message: `Required when ${describeCondition(condition)}` });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
};
|
|
488
523
|
const unknownKeys = { error: (issue) => issue.code === "unrecognized_keys" ? `Unknown field(s): ${issue.keys?.join(", ")}` : void 0 };
|
|
489
524
|
return {
|
|
490
|
-
create: z.strictObject(create, unknownKeys),
|
|
491
|
-
update: z.strictObject(update, unknownKeys)
|
|
525
|
+
create: z.strictObject(create, unknownKeys).superRefine(checkConditions(false)),
|
|
526
|
+
update: z.strictObject(update, unknownKeys).superRefine(checkConditions(true))
|
|
492
527
|
};
|
|
493
528
|
}
|
|
494
529
|
|
|
@@ -581,6 +616,8 @@ export {
|
|
|
581
616
|
defineResource,
|
|
582
617
|
storedFields,
|
|
583
618
|
columnName,
|
|
619
|
+
conditionHolds,
|
|
620
|
+
describeCondition,
|
|
584
621
|
flagEmoji,
|
|
585
622
|
countryName,
|
|
586
623
|
countries,
|
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, b as ClientResource, D as DEFAULT_FILE_MAX_BYTES, c as DateField, d as DateTimeField, E as EnumField, e as FILE_CATEGORIES, f as
|
|
1
|
+
import { F as Field, C as CreateInput, U as UpdateInput, R as Resource, S as StoredField } from './policy-DaK3dWQ_.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 FieldCondition, g as FieldKind, h as FieldValue, i as FileCategory, j as FileField, k as FloatField, H as HasManyField, I as IntField, J as JsonField, l as JsonOptions, M as MultiSelectField, N as NUMBER_FORMATS, m as NumberFormat, P as Policy, n as PolicyAction, o as PolicyConfig, p as PolicyError, q as PolicyOwnership, r as RESERVED_FIELD_NAMES, s as RecordOf, t as ResourceConfig, u as ResourceDefinitionError, v as STRING_FORMATS, w as StringField, x as StringFormat, T as TEXT_FORMATS, y as TagsField, z as TagsOptions, A as TextField, G as TextFormat, K as allowedActions, L as can, O as clientResource, Q as columnName, V as conditionHolds, W as defaultFieldLabel, X as definePolicy, Y as defineResource, Z as describeCondition, _ as field, $ as fileMaxBytes, a0 as mimeTypesFor, a1 as ownershipFilter, a2 as storedFields } from './policy-DaK3dWQ_.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -626,6 +626,6 @@ type SeedUser = (user: SeedUserInput) => Promise<CredentialUserRows["user"]>;
|
|
|
626
626
|
type Seed = (context: SeedContext) => void | Promise<void>;
|
|
627
627
|
declare function defineSeed(seed: Seed): Seed;
|
|
628
628
|
|
|
629
|
-
declare const FLARE_VERSION = "0.
|
|
629
|
+
declare const FLARE_VERSION = "0.11.0";
|
|
630
630
|
|
|
631
631
|
export { type BelongsToRelation, type Country, CreateInput, type CredentialUserRows, DEFAULT_MONEY_EXPONENT, 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 SeedUser, type SeedUserInput, 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, credentialUserRows, currentPeriodEnd, defineSeed, fake, fieldSchema, fileKeyPrefix, flagEmoji, formValuesToInput, formatPhone, formatValue, fromMinorUnits, grantsAccess, hasTooManyPlaces, hashPassword, humanize, importSigningKey, initialFormValues, isColor, isCountryCode, isDomain, isLiveSubscription, isPhoneNumber, isSlug, issuesByField, kebabCase, matchesContentType, moneyExponent, normalizeDomain, optionLabel, parseMultiValue, pascalCase, pluralize, randomLetters, relationGraph, renderTransactionalEmail, shouldApplySubscription, signToken, slugify, snakeCase, sniffMatches, splitPhone, statusTone, subscriptionPriceId, subscriptionState, toE164, toMinorUnits, toSubscriptionStatus, verifyPassword, verifyToken, words };
|
package/dist/index.js
CHANGED
|
@@ -13,12 +13,14 @@ import {
|
|
|
13
13
|
can,
|
|
14
14
|
clientResource,
|
|
15
15
|
columnName,
|
|
16
|
+
conditionHolds,
|
|
16
17
|
countries,
|
|
17
18
|
countryName,
|
|
18
19
|
createValidators,
|
|
19
20
|
defaultFieldLabel,
|
|
20
21
|
definePolicy,
|
|
21
22
|
defineResource,
|
|
23
|
+
describeCondition,
|
|
22
24
|
field,
|
|
23
25
|
fieldSchema,
|
|
24
26
|
fileKeyPrefix,
|
|
@@ -47,7 +49,7 @@ import {
|
|
|
47
49
|
toE164,
|
|
48
50
|
toMinorUnits,
|
|
49
51
|
words
|
|
50
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-62JI5DCR.js";
|
|
51
53
|
|
|
52
54
|
// src/password.ts
|
|
53
55
|
var ALGORITHM = "pbkdf2-sha256";
|
|
@@ -1052,7 +1054,7 @@ function createFake(seed = 20260101) {
|
|
|
1052
1054
|
var fake = createFake();
|
|
1053
1055
|
|
|
1054
1056
|
// src/index.ts
|
|
1055
|
-
var FLARE_VERSION = "0.
|
|
1057
|
+
var FLARE_VERSION = "0.11.0";
|
|
1056
1058
|
export {
|
|
1057
1059
|
DEFAULT_FILE_MAX_BYTES,
|
|
1058
1060
|
DEFAULT_MONEY_EXPONENT,
|
|
@@ -1074,6 +1076,7 @@ export {
|
|
|
1074
1076
|
checkoutPaid,
|
|
1075
1077
|
clientResource,
|
|
1076
1078
|
columnName,
|
|
1079
|
+
conditionHolds,
|
|
1077
1080
|
countries,
|
|
1078
1081
|
countryName,
|
|
1079
1082
|
createFake,
|
|
@@ -1087,6 +1090,7 @@ export {
|
|
|
1087
1090
|
definePolicy,
|
|
1088
1091
|
defineResource,
|
|
1089
1092
|
defineSeed,
|
|
1093
|
+
describeCondition,
|
|
1090
1094
|
fake,
|
|
1091
1095
|
field,
|
|
1092
1096
|
fieldSchema,
|
|
@@ -1,3 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fields that only apply sometimes.
|
|
3
|
+
*
|
|
4
|
+
* A billing cycle is required on a recurring project and meaningless on a one-off one.
|
|
5
|
+
* The usual way that gets built is an effect in the admin form that hides the input, and
|
|
6
|
+
* then the API happily accepts a cycle on a one-off project because nothing told it not
|
|
7
|
+
* to. The rule ends up in two or three places that disagree the first time somebody
|
|
8
|
+
* changes it.
|
|
9
|
+
*
|
|
10
|
+
* So the condition is declared on the field instead, once:
|
|
11
|
+
*
|
|
12
|
+
* billingCycle: field.enum(["monthly", "annually"], {
|
|
13
|
+
* showWhen: { field: "kind", op: "eq", value: "recurring" },
|
|
14
|
+
* })
|
|
15
|
+
*
|
|
16
|
+
* and everything that needs it reads the same object: the validator makes it required
|
|
17
|
+
* only when the condition holds, the store clears it when it doesn't, and the form shows
|
|
18
|
+
* the input only while it applies.
|
|
19
|
+
*/
|
|
20
|
+
interface FieldCondition {
|
|
21
|
+
/** The field this one depends on. Must be a field of the same resource. */
|
|
22
|
+
field: string;
|
|
23
|
+
op: "eq" | "ne" | "in";
|
|
24
|
+
/** A single value for eq/ne, a list for `in`. */
|
|
25
|
+
value: string | string[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Whether a conditional field applies, given the record it sits in.
|
|
29
|
+
*
|
|
30
|
+
* Compared as text, because the controlling field is an enum, a boolean or a number
|
|
31
|
+
* depending on who wrote the descriptor, and a form sends every one of them as a string.
|
|
32
|
+
* `true` and `"true"` have to answer the same here or the form and the API disagree.
|
|
33
|
+
*
|
|
34
|
+
* A field with no condition always applies, which is what makes this safe to call on
|
|
35
|
+
* every field without asking first.
|
|
36
|
+
*/
|
|
37
|
+
declare function conditionHolds(condition: FieldCondition | undefined, values: Record<string, unknown>): boolean;
|
|
38
|
+
/** "kind == recurring" — for an error message, a form hint or a generated comment. */
|
|
39
|
+
declare function describeCondition(condition: FieldCondition): string;
|
|
40
|
+
|
|
1
41
|
/**
|
|
2
42
|
* Field builders for resource descriptors.
|
|
3
43
|
*
|
|
@@ -6,6 +46,7 @@
|
|
|
6
46
|
* rule in a descriptor takes effect without re-running the generator. Only
|
|
7
47
|
* storage-level changes (kind, nullability, uniqueness, relations) need a migration.
|
|
8
48
|
*/
|
|
49
|
+
|
|
9
50
|
type FieldKind = "string" | "text" | "int" | "float" | "boolean" | "date" | "datetime" | "enum" | "multiselect" | "tags" | "json" | "file" | "belongsTo" | "hasMany";
|
|
10
51
|
/** Options shared by every stored (non-hasMany) field. */
|
|
11
52
|
interface CommonOptions<T> {
|
|
@@ -23,6 +64,14 @@ interface CommonOptions<T> {
|
|
|
23
64
|
filterable?: boolean;
|
|
24
65
|
/** Included in free-text search. Default true for string fields. */
|
|
25
66
|
searchable?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Only applies when another field says so, e.g. a billing cycle on a recurring project:
|
|
69
|
+
* `showWhen: { field: "kind", op: "eq", value: "recurring" }`.
|
|
70
|
+
*
|
|
71
|
+
* The column is always nullable, because the field is genuinely absent the rest of the
|
|
72
|
+
* time. `required` then means "required *when it applies*" rather than "never null".
|
|
73
|
+
*/
|
|
74
|
+
showWhen?: FieldCondition;
|
|
26
75
|
}
|
|
27
76
|
/**
|
|
28
77
|
* What a string holds. Each format is still a text column; the format decides
|
|
@@ -508,4 +557,4 @@ declare function ownershipFilter(policy: Policy | undefined | null, user: {
|
|
|
508
557
|
/** Actions `role` may perform, for hiding UI it can't use. */
|
|
509
558
|
declare function allowedActions(policy: Policy | undefined | null, role: string | null | undefined): Record<Exclude<PolicyAction, "list">, boolean>;
|
|
510
559
|
|
|
511
|
-
export {
|
|
560
|
+
export { fileMaxBytes as $, type TextField as A, type BelongsToField as B, type CreateInput as C, DEFAULT_FILE_MAX_BYTES as D, type EnumField as E, type Field as F, type TextFormat as G, type HasManyField as H, type IntField as I, type JsonField as J, allowedActions as K, can as L, type MultiSelectField as M, NUMBER_FORMATS as N, clientResource as O, type Policy as P, columnName as Q, type Resource as R, type StoredField as S, TEXT_FORMATS as T, type UpdateInput as U, conditionHolds as V, defaultFieldLabel as W, definePolicy as X, defineResource as Y, describeCondition as Z, field as _, type BooleanField as a, mimeTypesFor as a0, ownershipFilter as a1, storedFields as a2, type ClientResource as b, type DateField as c, type DateTimeField as d, FILE_CATEGORIES as e, type FieldCondition as f, type FieldKind as g, type FieldValue as h, type FileCategory as i, type FileField as j, type FloatField as k, type JsonOptions as l, type NumberFormat as m, type PolicyAction as n, type PolicyConfig as o, PolicyError as p, type PolicyOwnership as q, RESERVED_FIELD_NAMES as r, type RecordOf as s, type ResourceConfig as t, ResourceDefinitionError as u, STRING_FORMATS as v, type StringField as w, type StringFormat as x, type TagsField as y, type TagsOptions 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, SQLiteColumn } 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-DaK3dWQ_.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The handful of things a resource store needs a database to do.
|
package/dist/server.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
createValidators,
|
|
5
5
|
moneyExponent,
|
|
6
6
|
storedFields
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-62JI5DCR.js";
|
|
8
8
|
|
|
9
9
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudfl_21866ab18c10d2d988b916e06105fbce/node_modules/drizzle-orm/entity.js
|
|
10
10
|
var entityKind = /* @__PURE__ */ Symbol.for("drizzle:entityKind");
|