@flaredev/core 0.12.2 → 0.14.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.
@@ -907,6 +907,46 @@ function allowedActions(policy, role) {
907
907
  };
908
908
  }
909
909
 
910
+ // src/resource/grants.ts
911
+ function isRestricted(policy) {
912
+ return Boolean(policy);
913
+ }
914
+ function withGrants(policy, resource, grants) {
915
+ if (!policy) return void 0;
916
+ const added = {
917
+ read: new Set(policy.read),
918
+ create: new Set(policy.create),
919
+ update: new Set(policy.update),
920
+ delete: new Set(policy.delete)
921
+ };
922
+ let changed = false;
923
+ for (const grant of grants) {
924
+ if (grant.resource !== resource) continue;
925
+ const bucket = added[grant.action];
926
+ if (!bucket || bucket.has(grant.role)) continue;
927
+ bucket.add(grant.role);
928
+ changed = true;
929
+ }
930
+ if (!changed) return policy;
931
+ return {
932
+ ...policy,
933
+ read: [...added.read],
934
+ create: [...added.create],
935
+ update: [...added.update],
936
+ delete: [...added.delete]
937
+ };
938
+ }
939
+ function grantIndex(grants) {
940
+ const index = /* @__PURE__ */ new Map();
941
+ for (const grant of grants) {
942
+ const key = `${grant.resource}.${grant.action}`;
943
+ const roles = index.get(key) ?? /* @__PURE__ */ new Set();
944
+ roles.add(grant.role);
945
+ index.set(key, roles);
946
+ }
947
+ return index;
948
+ }
949
+
910
950
  export {
911
951
  STRING_FORMATS,
912
952
  TEXT_FORMATS,
@@ -972,5 +1012,8 @@ export {
972
1012
  definePolicy,
973
1013
  can,
974
1014
  ownershipFilter,
975
- allowedActions
1015
+ allowedActions,
1016
+ isRestricted,
1017
+ withGrants,
1018
+ grantIndex
976
1019
  };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { R as Rate, b as RateLimitConfig, a as RateLimitDecision, c as RateLimitStore, d as RateLimitedAction, e as RateSyntaxError, f as fixedWindow, p as parseRate, r as rateFor, g as rateLimitKey, h as ratesIn } from './rate-limit-ClbdZioJ.js';
2
- import { F as Field, C as CreateInput, U as UpdateInput, R as Resource, S as StoredField } from './policy-b4FXN3ZJ.js';
3
- export { B as BelongsToField, f as BooleanField, g as ClientResource, D as DEFAULT_FILE_MAX_BYTES, h as DateField, i as DateTimeField, E as EnumField, j as FILE_CATEGORIES, k as FieldCondition, l as FieldKind, m as FieldValue, n as FileCategory, o as FileField, q as FloatField, H as HasManyField, I as ISSUE_CODES, r as IntField, c as IssueCode, J as JsonField, s as JsonOptions, M as MultiSelectField, N as NUMBER_FORMATS, t as NumberFormat, d as PROBLEM_CODES, b as Policy, u as PolicyAction, v as PolicyConfig, x as PolicyError, y as PolicyOwnership, e as ProblemBody, P as ProblemCode, a as ProblemIssue, z as RESERVED_FIELD_NAMES, A as RecordOf, G as ResourceConfig, K as ResourceDefinitionError, L as STRING_FORMATS, O as StringField, Q as StringFormat, T as TEXT_FORMATS, V as TagsField, W as TagsOptions, X as TextField, Y as TextFormat, Z as allowedActions, _ as can, $ as clientResource, a0 as columnName, a1 as conditionHolds, a2 as defaultFieldLabel, a3 as definePolicy, a4 as defineResource, a5 as describeCondition, a6 as field, a7 as fileMaxBytes, a8 as mimeTypesFor, a9 as ownershipFilter, p as problemResponse, aa as storedFields, w as wantsJson } from './policy-b4FXN3ZJ.js';
2
+ import { F as Field, C as CreateInput, U as UpdateInput, R as Resource, S as StoredField, f as PolicyAction, b as Policy } from './policy-eVpUpYxV.js';
3
+ export { B as BelongsToField, g as BooleanField, h as ClientResource, D as DEFAULT_FILE_MAX_BYTES, i as DateField, j as DateTimeField, E as EnumField, k as FILE_CATEGORIES, l as FieldCondition, m as FieldKind, n as FieldValue, o as FileCategory, q as FileField, r as FloatField, H as HasManyField, I as ISSUE_CODES, s as IntField, c as IssueCode, J as JsonField, t as JsonOptions, M as MultiSelectField, N as NUMBER_FORMATS, u as NumberFormat, d as PROBLEM_CODES, v as PolicyConfig, x as PolicyError, y as PolicyOwnership, e as ProblemBody, P as ProblemCode, a as ProblemIssue, z as RESERVED_FIELD_NAMES, A as RecordOf, G as ResourceConfig, K as ResourceDefinitionError, L as STRING_FORMATS, O as StringField, Q as StringFormat, T as TEXT_FORMATS, V as TagsField, W as TagsOptions, X as TextField, Y as TextFormat, Z as allowedActions, _ as can, $ as clientResource, a0 as columnName, a1 as conditionHolds, a2 as defaultFieldLabel, a3 as definePolicy, a4 as defineResource, a5 as describeCondition, a6 as field, a7 as fileMaxBytes, a8 as mimeTypesFor, a9 as ownershipFilter, p as problemResponse, aa as storedFields, w as wantsJson } from './policy-eVpUpYxV.js';
4
4
  import { z } from 'zod';
5
5
 
6
6
  /**
@@ -591,6 +591,49 @@ declare const isSlug: (value: string) => boolean;
591
591
  /** "My First Post!" → "my-first-post". */
592
592
  declare function slugify(value: string): string;
593
593
 
594
+ /**
595
+ * Permissions granted from the dashboard, on top of the policies in your code.
596
+ *
597
+ * Flare's policies are files — `policies/invoice.policy.ts` — and that is deliberate:
598
+ * who may do what is reviewable in a pull request, travels with the repository, and is
599
+ * the same on every machine before anything is deployed. Managing roles from a running
600
+ * app means permissions in a database, and a database is none of those things.
601
+ *
602
+ * So the two compose rather than compete. **The policy file is the floor and the
603
+ * database only ever widens it.** A grant adds a role to an action; nothing in the
604
+ * dashboard can take away access the code gives, and nothing in the dashboard can make
605
+ * a resource restricted that the code left open. That last point matters more than it
606
+ * sounds — see {@link withGrants}.
607
+ */
608
+ /** One row of "this role may do this to this resource". */
609
+ interface PermissionGrant {
610
+ role: string;
611
+ /** The resource's descriptor name, e.g. "Invoice". */
612
+ resource: string;
613
+ /** "list" is read, so it is not a grant of its own. */
614
+ action: Exclude<PolicyAction, "list">;
615
+ }
616
+ /** Whether a resource is restricted at all — i.e. whether granting on it does anything. */
617
+ declare function isRestricted(policy: Policy | undefined | null): boolean;
618
+ /**
619
+ * A resource's policy with dashboard grants folded in.
620
+ *
621
+ * Returns `undefined` unchanged when there is no policy file, and that is the whole
622
+ * subtlety of this feature: a resource with no policy is open to every signed-in user,
623
+ * so a grant on it would be a tick-box that changes nothing. Pretending otherwise — by
624
+ * inventing a policy from the grants — would be worse than useless: an app would start
625
+ * refusing people the moment somebody opened the roles screen, and the reason would be
626
+ * in a table nobody reads. The UI's job is to say "this resource is open; restrict it
627
+ * with `flare gen policy`", and this function's job is to not lie.
628
+ */
629
+ declare function withGrants(policy: Policy | undefined | null, resource: string, grants: Iterable<PermissionGrant>): Policy | undefined;
630
+ /**
631
+ * Every (resource, action) a role is granted, as a lookup the UI can tick boxes from.
632
+ *
633
+ * Keyed `"Resource.action"` because that is what a matrix cell is.
634
+ */
635
+ declare function grantIndex(grants: Iterable<PermissionGrant>): Map<string, Set<string>>;
636
+
594
637
  /**
595
638
  * Sample data for seeds. Small on purpose: a handful of word lists and a seeded
596
639
  * random number generator, so a million rows cost nothing to produce and the same
@@ -742,6 +785,6 @@ type SeedUser = (user: SeedUserInput) => Promise<CredentialUserRows["user"]>;
742
785
  type Seed = (context: SeedContext) => void | Promise<void>;
743
786
  declare function defineSeed(seed: Seed): Seed;
744
787
 
745
- declare const FLARE_VERSION = "0.12.2";
788
+ declare const FLARE_VERSION = "0.14.0";
746
789
 
747
- export { AUTH_USER, type BelongsToRelation, type Country, CreateInput, type CredentialUserRows, DEFAULT_MONEY_EXPONENT, type EnumTranslation, 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 StorageObjectBody, 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, defaultAuthUserOnDelete, defineSeed, enumMemberCollision, enumTranslations, fake, fieldSchema, fileKeyPrefix, flagEmoji, formValuesToInput, formatPhone, formatValue, fromMinorUnits, grantsAccess, hasTooManyPlaces, hashPassword, humanize, importSigningKey, initialFormValues, isAuthUserTarget, isColor, isCountryCode, isDomain, isLiveSubscription, isPhoneNumber, isSlug, issuesByField, kebabCase, matchesContentType, moneyExponent, normalizeDomain, optionLabel, parseMultiValue, pascalCase, pluralize, prismaEnumMember, randomLetters, relationGraph, renderTransactionalEmail, shouldApplySubscription, signToken, slugify, snakeCase, sniffMatches, splitPhone, statusTone, subscriptionPriceId, subscriptionState, toE164, toMinorUnits, toSubscriptionStatus, verifyPassword, verifyToken, words };
790
+ export { AUTH_USER, type BelongsToRelation, type Country, CreateInput, type CredentialUserRows, DEFAULT_MONEY_EXPONENT, type EnumTranslation, FLARE_VERSION, type Fake, Field, type FormValues, type FormatOptions, type HasManyRelation, type InsertMany, type MailMessage, type MailResult, type Mailer, type MailerOptions, PBKDF2_ITERATIONS, type PermissionGrant, Policy, PolicyAction, 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 StorageObjectBody, 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, defaultAuthUserOnDelete, defineSeed, enumMemberCollision, enumTranslations, fake, fieldSchema, fileKeyPrefix, flagEmoji, formValuesToInput, formatPhone, formatValue, fromMinorUnits, grantIndex, grantsAccess, hasTooManyPlaces, hashPassword, humanize, importSigningKey, initialFormValues, isAuthUserTarget, isColor, isCountryCode, isDomain, isLiveSubscription, isPhoneNumber, isRestricted, isSlug, issuesByField, kebabCase, matchesContentType, moneyExponent, normalizeDomain, optionLabel, parseMultiValue, pascalCase, pluralize, prismaEnumMember, randomLetters, relationGraph, renderTransactionalEmail, shouldApplySubscription, signToken, slugify, snakeCase, sniffMatches, splitPhone, statusTone, subscriptionPriceId, subscriptionState, toE164, toMinorUnits, toSubscriptionStatus, verifyPassword, verifyToken, withGrants, words };
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ import {
35
35
  formatPhone,
36
36
  formatValue,
37
37
  fromMinorUnits,
38
+ grantIndex,
38
39
  hasTooManyPlaces,
39
40
  humanize,
40
41
  initialFormValues,
@@ -43,6 +44,7 @@ import {
43
44
  isCountryCode,
44
45
  isDomain,
45
46
  isPhoneNumber,
47
+ isRestricted,
46
48
  isSlug,
47
49
  issuesByField,
48
50
  kebabCase,
@@ -63,8 +65,9 @@ import {
63
65
  storedFields,
64
66
  toE164,
65
67
  toMinorUnits,
68
+ withGrants,
66
69
  words
67
- } from "./chunk-BTVEH5AU.js";
70
+ } from "./chunk-QEK4KWKL.js";
68
71
  import {
69
72
  ISSUE_CODES,
70
73
  PROBLEM_CODES,
@@ -892,7 +895,7 @@ function createFake(seed = 20260101) {
892
895
  var fake = createFake();
893
896
 
894
897
  // src/index.ts
895
- var FLARE_VERSION = "0.12.2";
898
+ var FLARE_VERSION = "0.14.0";
896
899
  export {
897
900
  AUTH_USER,
898
901
  DEFAULT_FILE_MAX_BYTES,
@@ -947,6 +950,7 @@ export {
947
950
  formatPhone,
948
951
  formatValue,
949
952
  fromMinorUnits,
953
+ grantIndex,
950
954
  grantsAccess,
951
955
  hasTooManyPlaces,
952
956
  hashPassword,
@@ -959,6 +963,7 @@ export {
959
963
  isDomain,
960
964
  isLiveSubscription,
961
965
  isPhoneNumber,
966
+ isRestricted,
962
967
  isSlug,
963
968
  issuesByField,
964
969
  kebabCase,
@@ -996,5 +1001,6 @@ export {
996
1001
  verifyPassword,
997
1002
  verifyToken,
998
1003
  wantsJson,
1004
+ withGrants,
999
1005
  words
1000
1006
  };
@@ -702,4 +702,4 @@ declare function ownershipFilter(policy: Policy | undefined | null, user: {
702
702
  /** Actions `role` may perform, for hiding UI it can't use. */
703
703
  declare function allowedActions(policy: Policy | undefined | null, role: string | null | undefined): Record<Exclude<PolicyAction, "list">, boolean>;
704
704
 
705
- export { clientResource as $, type RecordOf 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 ResourceConfig as G, type HasManyField as H, ISSUE_CODES as I, type JsonField as J, ResourceDefinitionError as K, STRING_FORMATS as L, type MultiSelectField as M, NUMBER_FORMATS as N, type StringField as O, type ProblemCode as P, type StringFormat as Q, type Resource as R, type StoredField as S, TEXT_FORMATS as T, type UpdateInput as U, type TagsField as V, type TagsOptions as W, type TextField as X, type TextFormat as Y, allowedActions as Z, can as _, type ProblemIssue as a, columnName as a0, conditionHolds as a1, defaultFieldLabel as a2, definePolicy as a3, defineResource as a4, describeCondition as a5, field as a6, fileMaxBytes as a7, mimeTypesFor as a8, ownershipFilter as a9, storedFields as aa, type Policy as b, type IssueCode as c, PROBLEM_CODES as d, type ProblemBody as e, type BooleanField as f, type ClientResource as g, type DateField as h, type DateTimeField as i, FILE_CATEGORIES as j, type FieldCondition as k, type FieldKind as l, type FieldValue as m, type FileCategory as n, type FileField as o, problemResponse as p, type FloatField as q, type IntField as r, type JsonOptions as s, type NumberFormat as t, type PolicyAction as u, type PolicyConfig as v, wantsJson as w, PolicyError as x, type PolicyOwnership as y, RESERVED_FIELD_NAMES as z };
705
+ export { clientResource as $, type RecordOf 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 ResourceConfig as G, type HasManyField as H, ISSUE_CODES as I, type JsonField as J, ResourceDefinitionError as K, STRING_FORMATS as L, type MultiSelectField as M, NUMBER_FORMATS as N, type StringField as O, type ProblemCode as P, type StringFormat as Q, type Resource as R, type StoredField as S, TEXT_FORMATS as T, type UpdateInput as U, type TagsField as V, type TagsOptions as W, type TextField as X, type TextFormat as Y, allowedActions as Z, can as _, type ProblemIssue as a, columnName as a0, conditionHolds as a1, defaultFieldLabel as a2, definePolicy as a3, defineResource as a4, describeCondition as a5, field as a6, fileMaxBytes as a7, mimeTypesFor as a8, ownershipFilter as a9, storedFields as aa, type Policy as b, type IssueCode as c, PROBLEM_CODES as d, type ProblemBody as e, type PolicyAction as f, type BooleanField as g, type ClientResource as h, type DateField as i, type DateTimeField as j, FILE_CATEGORIES as k, type FieldCondition as l, type FieldKind as m, type FieldValue as n, type FileCategory as o, problemResponse as p, type FileField as q, type FloatField as r, type IntField as s, type JsonOptions as t, type NumberFormat as u, type PolicyConfig as v, wantsJson as w, PolicyError as x, type PolicyOwnership as y, RESERVED_FIELD_NAMES as z };
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
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 { P as ProblemCode, a as ProblemIssue, R as Resource, F as Field, S as StoredField, b as Policy } from './policy-b4FXN3ZJ.js';
4
- export { I as ISSUE_CODES, c as IssueCode, d as PROBLEM_CODES, e as ProblemBody, p as problemResponse, w as wantsJson } from './policy-b4FXN3ZJ.js';
3
+ import { P as ProblemCode, a as ProblemIssue, R as Resource, F as Field, S as StoredField, b as Policy } from './policy-eVpUpYxV.js';
4
+ export { I as ISSUE_CODES, c as IssueCode, d as PROBLEM_CODES, e as ProblemBody, p as problemResponse, w as wantsJson } from './policy-eVpUpYxV.js';
5
5
  import './rate-limit-ClbdZioJ.js';
6
6
 
7
7
  /**
package/dist/server.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  enumTranslations,
6
6
  moneyExponent,
7
7
  storedFields
8
- } from "./chunk-BTVEH5AU.js";
8
+ } from "./chunk-QEK4KWKL.js";
9
9
  import {
10
10
  ISSUE_CODES,
11
11
  PROBLEM_CODES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flaredev/core",
3
- "version": "0.12.2",
3
+ "version": "0.14.0",
4
4
  "description": "Flare runtime: resource descriptors, field grammar, and Cloudflare-native helpers",
5
5
  "license": "MIT",
6
6
  "author": "Muke Johnbaptist",