@fitzzero/quickdraw-core 5.0.0 → 5.0.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.
- package/README.md +40 -17
- package/dist/{admin-BiU4LRZS.d.ts → admin-CBlTY0do.d.ts} +24 -9
- package/dist/{adminSchemas-k5a1Ov7b.d.ts → adminSchemas-zji11V74.d.ts} +14 -8
- package/dist/{caller-DHzK3oiI.d.ts → caller-BqCYnpmC.d.ts} +1 -1
- package/dist/{chunk-5IC5VZ7T.js → chunk-3JNMVB2V.js} +3 -3
- package/dist/{chunk-DNCAI3KY.js → chunk-4DIQHASK.js} +1 -1
- package/dist/chunk-4DIQHASK.js.map +1 -0
- package/dist/{chunk-B2UQ27GH.js → chunk-CDKG4TFA.js} +25 -5
- package/dist/{chunk-B2UQ27GH.js.map → chunk-CDKG4TFA.js.map} +1 -1
- package/dist/{chunk-XFDOGTMC.js → chunk-HQ6WIRE2.js} +3 -3
- package/dist/{chunk-LLLYILEP.js → chunk-NS5PUHIZ.js} +283 -22
- package/dist/chunk-NS5PUHIZ.js.map +1 -0
- package/dist/{chunk-XUSN5WKU.js → chunk-O2PWC2YW.js} +3 -3
- package/dist/{chunk-7QIY2DDX.js → chunk-WVIRXYI4.js} +2 -2
- package/dist/{chunk-7QIY2DDX.js.map → chunk-WVIRXYI4.js.map} +1 -1
- package/dist/{chunk-IRGAGRVG.js → chunk-ZACRRYWL.js} +3 -3
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.js +4 -4
- package/dist/{clientTypes-B3o_99Xq.d.ts → clientTypes-DuZW_8mb.d.ts} +1 -1
- package/dist/{createServer-DUacdfKl.d.ts → createServer-CGsJlxM8.d.ts} +2 -2
- package/dist/{createTestApp-DFLXoTKm.d.ts → createTestApp-Cg2CZa1g.d.ts} +3 -3
- package/dist/{http--uoooIH5.d.ts → http-D3fSFQ57.d.ts} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/prisma/index.d.ts +1 -1
- package/dist/prisma/index.js +1 -1
- package/dist/server/auth/index.d.ts +11 -2
- package/dist/server/auth/index.js +0 -5
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/index.d.ts +83 -17
- package/dist/server/index.js +87 -160
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +2 -2
- package/dist/{sharing-n4ss-ONM.d.ts → sharing-CoreezAq.d.ts} +2 -2
- package/dist/testing/client.d.ts +7 -7
- package/dist/testing/client.js +5 -5
- package/dist/testing/index.d.ts +6 -6
- package/dist/testing/index.js +3 -3
- package/dist/testing/mock.d.ts +3 -3
- package/dist/testing/mock.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-DNCAI3KY.js.map +0 -1
- package/dist/chunk-LLLYILEP.js.map +0 -1
- /package/dist/{chunk-5IC5VZ7T.js.map → chunk-3JNMVB2V.js.map} +0 -0
- /package/dist/{chunk-XFDOGTMC.js.map → chunk-HQ6WIRE2.js.map} +0 -0
- /package/dist/{chunk-XUSN5WKU.js.map → chunk-O2PWC2YW.js.map} +0 -0
- /package/dist/{chunk-IRGAGRVG.js.map → chunk-ZACRRYWL.js.map} +0 -0
package/dist/server/index.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { J as AnyContract, at as ProjectionName, ai as FullProjectionOf, ac as CollectionName, ap as MethodName, d as StandardSchemaV1, c as InferOutput, N as NullableProjection, P as ProjectionList, aq as MethodOf, as as ParsedInputOf, ao as KindOf, A as AccessLevel, af as EntityOf, ar as OutputOf } from '../infer-Dw21kmLw.js';
|
|
2
|
-
import { M as ModelName, v as PolicyFor, w as RowForms, x as AccessFor, H as HandlerContext, y as ModelColumn, z as WatchAccess, B as PublicAccess, E as HandlerArgs, G as CustomAccess, I as ShareMode, J as ChannelOptions, K as StreamOptions, u as RoomLeaveHandler, L as ChannelsRequired, f as Service, N as BaseContext, O as CallerFor, l as RunContext, m as RunOptions, o as StreamHandle, P as Presence, n as ServerRooms, Q as AccessPolicy, T as AnyAccessPolicy, V as PolicyColumns, X as PolicyForeign, Y as PolicyTools, k as AccessFilter, Z as RowAccess, q as AccessEngine, b as StorageWhere } from '../caller-
|
|
3
|
-
export { aI as ANY_FIELD, aA as AccessForm, aB as AccessRequest, _ as AffectsLink, p as AnyContext, A as AnyService, r as AppRooms, aC as AuthenticatedAccess, aq as CallOptions, i as Caller, a9 as ChannelAccess, aa as ChannelContext, ab as ChannelHandler, ac as ChannelImplementation, ad as ChannelRoomOf, a4 as CollectionScope, a6 as ContextRooms, a7 as ContextServices, C as CountArgs, a3 as DEFAULT_BULK_THRESHOLD, g as DevWarning, D as DevWarningError, h as DevWarningKind, aD as EntryAccess, F as FindManyArgs, e as FlushInfo, d as FlushSink, at as ForeignColumns, au as ForeignColumnsOf, aE as IdKeyOf, aF as IdSelector, av as MembershipRead, ar as MethodCaller, aM as N_PLUS_ONE_STATEMENTS, aw as ParentLink, ax as PolicyKind, ay as PolicyReads, $ as ProjectedOutput, a0 as Projection, ae as RoomLeave, af as RoomLeaveReason, ag as RoomLeft, ah as RoomTarget, az as RowLevel, R as RowLevels, aG as ScopeAccess, aH as ServiceAccess, as as ServiceCaller, ai as ServiceChannel, a5 as ServiceCollection, j as ServiceMethod, aj as ServiceStream, c as StatementCount, S as StorageAdapter, a as StorageRow, ak as StreamImplementation, al as StreamPushArgs, am as StreamPushManyArgs, an as StreamSeed, ao as StreamSeedContext, t as StreamSubscriptions, ap as StreamValidation, a1 as TierGroup, a2 as Tiers, a8 as TouchOptions, aJ as UnitOfWork, U as UnitOfWorkFactory, aK as UnitOfWorkScope, W as WriteRecord, aL as formatDevWarning, s as storageOf } from '../caller-
|
|
4
|
-
import { S as ServerOptions, Q as QuickdrawServer } from '../createServer-
|
|
5
|
-
export { C as ClusterClient, t as ClusterOptions, D as DEFAULT_STALL_INTERVAL_MS, e as DEFAULT_STALL_MAX_MS, f as DEFAULT_STALL_THRESHOLD_MS, p as DisconnectUserOptions, E as EntitySubscription, a as EntitySubscriptions, H as HttpApp, I as IoRedisLike, M as MIN_STALL_INTERVAL_MS, m as QuickdrawIo, n as QuickdrawServerSocket, o as QuickdrawSocketData, x as RateLimitOptions, y as RateLimiter, R as RotateOptions, g as STALL_RESOLUTION_MS, b as ScopeSubscription, c as ScopeSubscriptions, l as ServerOnlyOptions, h as SlowMethod, q as SocketCors, r as SocketOptions, s as SocketRateLimitOptions, i as StallReport, j as StallWatchdogOptions, T as TopicWatch, d as TopicWatches, v as applyRateLimitMiddleware, u as createRateLimiter, k as createServer, w as createTieredRateLimiter } from '../createServer-
|
|
2
|
+
import { M as ModelName, v as PolicyFor, w as RowForms, x as AccessFor, H as HandlerContext, y as ModelColumn, z as WatchAccess, B as PublicAccess, E as HandlerArgs, G as CustomAccess, I as ShareMode, J as ChannelOptions, K as StreamOptions, u as RoomLeaveHandler, L as ChannelsRequired, f as Service, N as BaseContext, O as CallerFor, l as RunContext, m as RunOptions, o as StreamHandle, P as Presence, n as ServerRooms, Q as AccessPolicy, T as AnyAccessPolicy, V as PolicyColumns, X as PolicyForeign, Y as PolicyTools, k as AccessFilter, Z as RowAccess, q as AccessEngine, b as StorageWhere } from '../caller-BqCYnpmC.js';
|
|
3
|
+
export { aI as ANY_FIELD, aA as AccessForm, aB as AccessRequest, _ as AffectsLink, p as AnyContext, A as AnyService, r as AppRooms, aC as AuthenticatedAccess, aq as CallOptions, i as Caller, a9 as ChannelAccess, aa as ChannelContext, ab as ChannelHandler, ac as ChannelImplementation, ad as ChannelRoomOf, a4 as CollectionScope, a6 as ContextRooms, a7 as ContextServices, C as CountArgs, a3 as DEFAULT_BULK_THRESHOLD, g as DevWarning, D as DevWarningError, h as DevWarningKind, aD as EntryAccess, F as FindManyArgs, e as FlushInfo, d as FlushSink, at as ForeignColumns, au as ForeignColumnsOf, aE as IdKeyOf, aF as IdSelector, av as MembershipRead, ar as MethodCaller, aM as N_PLUS_ONE_STATEMENTS, aw as ParentLink, ax as PolicyKind, ay as PolicyReads, $ as ProjectedOutput, a0 as Projection, ae as RoomLeave, af as RoomLeaveReason, ag as RoomLeft, ah as RoomTarget, az as RowLevel, R as RowLevels, aG as ScopeAccess, aH as ServiceAccess, as as ServiceCaller, ai as ServiceChannel, a5 as ServiceCollection, j as ServiceMethod, aj as ServiceStream, c as StatementCount, S as StorageAdapter, a as StorageRow, ak as StreamImplementation, al as StreamPushArgs, am as StreamPushManyArgs, an as StreamSeed, ao as StreamSeedContext, t as StreamSubscriptions, ap as StreamValidation, a1 as TierGroup, a2 as Tiers, a8 as TouchOptions, aJ as UnitOfWork, U as UnitOfWorkFactory, aK as UnitOfWorkScope, W as WriteRecord, aL as formatDevWarning, s as storageOf } from '../caller-BqCYnpmC.js';
|
|
4
|
+
import { S as ServerOptions, Q as QuickdrawServer } from '../createServer-CGsJlxM8.js';
|
|
5
|
+
export { C as ClusterClient, t as ClusterOptions, D as DEFAULT_STALL_INTERVAL_MS, e as DEFAULT_STALL_MAX_MS, f as DEFAULT_STALL_THRESHOLD_MS, p as DisconnectUserOptions, E as EntitySubscription, a as EntitySubscriptions, H as HttpApp, I as IoRedisLike, M as MIN_STALL_INTERVAL_MS, m as QuickdrawIo, n as QuickdrawServerSocket, o as QuickdrawSocketData, x as RateLimitOptions, y as RateLimiter, R as RotateOptions, g as STALL_RESOLUTION_MS, b as ScopeSubscription, c as ScopeSubscriptions, l as ServerOnlyOptions, h as SlowMethod, q as SocketCors, r as SocketOptions, s as SocketRateLimitOptions, i as StallReport, j as StallWatchdogOptions, T as TopicWatch, d as TopicWatches, v as applyRateLimitMiddleware, u as createRateLimiter, k as createServer, w as createTieredRateLimiter } from '../createServer-CGsJlxM8.js';
|
|
6
6
|
import { ad as Version } from '../envelope-nf5eGB-a.js';
|
|
7
7
|
import { Q as QuickdrawTypes, D as DbOf, c as PrincipalOf, M as MaybePromise, b as McpContextOf, C as ContextExtensionOf, P as Principal } from '../types-Bwg3kIEc.js';
|
|
8
8
|
export { a as McpContext, T as Transport } from '../types-Bwg3kIEc.js';
|
|
9
|
-
import { a as DispatcherOptions, D as Dispatcher, c as DispatcherCollections } from '../http
|
|
10
|
-
export { o as AccessChange, p as AccessChangeListener, A as AccessOptions, d as ChangeLogOptions, C as ContractOfServices, j as DEFAULT_LIMITS, f as DbOfServices, h as DispatchRequest, i as DispatchResult, n as DispatcherAccess, k as DispatcherLimits, s as HttpMiddleware, H as HttpRouter, u as HttpRouterOptions, b as HttpTransportOptions, L as LimitsOptions, l as PipelineOptions, P as PrincipalOfServices, R as RegisteredMethod, g as Registry, S as SharedData, m as SubscriptionLimits, T as TrackingOptions, V as VersionRequest, q as VersionSource, e as createDispatcher, r as createHttpRouter, t as toCallReply } from '../http
|
|
11
|
-
import { l as CrudMethodName, d as CrudContractOptions, S as ScalarFieldOf, N as NumberFieldOf, m as CrudMethods, H as AdminMethodsOf, w as AdminContractOptions, G as AdminMethods } from '../admin-
|
|
12
|
-
import { S as SearchContractOptions, T as TextFieldOf, b as SearchMethods, n as SharingMethodName, f as SharingByNameMethod, g as SharingContractOptions, o as SharingMethods } from '../sharing-
|
|
13
|
-
import { l as AdminFieldConfig } from '../adminSchemas-
|
|
9
|
+
import { a as DispatcherOptions, D as Dispatcher, c as DispatcherCollections } from '../http-D3fSFQ57.js';
|
|
10
|
+
export { o as AccessChange, p as AccessChangeListener, A as AccessOptions, d as ChangeLogOptions, C as ContractOfServices, j as DEFAULT_LIMITS, f as DbOfServices, h as DispatchRequest, i as DispatchResult, n as DispatcherAccess, k as DispatcherLimits, s as HttpMiddleware, H as HttpRouter, u as HttpRouterOptions, b as HttpTransportOptions, L as LimitsOptions, l as PipelineOptions, P as PrincipalOfServices, R as RegisteredMethod, g as Registry, S as SharedData, m as SubscriptionLimits, T as TrackingOptions, V as VersionRequest, q as VersionSource, e as createDispatcher, r as createHttpRouter, t as toCallReply } from '../http-D3fSFQ57.js';
|
|
11
|
+
import { l as CrudMethodName, d as CrudContractOptions, S as ScalarFieldOf, N as NumberFieldOf, m as CrudMethods, H as AdminMethodsOf, w as AdminContractOptions, G as AdminMethods } from '../admin-CBlTY0do.js';
|
|
12
|
+
import { S as SearchContractOptions, T as TextFieldOf, b as SearchMethods, n as SharingMethodName, f as SharingByNameMethod, g as SharingContractOptions, o as SharingMethods } from '../sharing-CoreezAq.js';
|
|
13
|
+
import { x as AdminNeverWritable, l as AdminFieldConfig } from '../adminSchemas-zji11V74.js';
|
|
14
14
|
export { a as CallOutcome, C as CallRecord } from '../metrics-C0WvB7-I.js';
|
|
15
15
|
export { b as AuthenticateRequest, A as AuthenticateResult, c as HttpAuthenticateRequest, d as HttpCredentialSource, S as ServerAuth, e as ServiceAccessSource, a as ServiceGrants, f as SocketAuthenticateRequest, r as recordSocketSession } from '../auth-CMyRkfjZ.js';
|
|
16
16
|
import { Server } from 'socket.io';
|
|
@@ -616,8 +616,25 @@ declare function inherit<const Via extends string>(options: InheritOptions<Via>)
|
|
|
616
616
|
*/
|
|
617
617
|
declare function anyOf<const P extends readonly AnyAccessPolicy[]>(...policies: P): AccessPolicy<PolicyColumns<P[number]>, PolicyForeign<P[number]>>;
|
|
618
618
|
|
|
619
|
+
/** A membership table a resolver's levels depend on: `{ model, entry, user, level, levels? }`, as `members` takes it. */
|
|
620
|
+
type ResolverMembership = MembersOptions;
|
|
621
|
+
/** What a resolver's levels depend on: `resolver({ reads })`. Name at least one column or table. */
|
|
622
|
+
interface ResolverReads<Column extends string = string, Membership extends ResolverMembership = ResolverMembership> {
|
|
623
|
+
/**
|
|
624
|
+
* Columns of the service's own model. A tracked write that sets one (or
|
|
625
|
+
* creates or deletes a row) re-checks that row, for every user. `tools.rows`
|
|
626
|
+
* reads them.
|
|
627
|
+
*/
|
|
628
|
+
readonly columns?: readonly Column[];
|
|
629
|
+
/**
|
|
630
|
+
* Membership tables, one row per user and row of this service: `entry`
|
|
631
|
+
* holds the id of this service's row, as for `members`. A tracked write to
|
|
632
|
+
* one re-checks that user on that row.
|
|
633
|
+
*/
|
|
634
|
+
readonly memberships?: readonly Membership[];
|
|
635
|
+
}
|
|
619
636
|
/** Options of {@link resolver}. */
|
|
620
|
-
interface ResolverOptions<P extends Principal = Principal> {
|
|
637
|
+
interface ResolverOptions<P extends Principal = Principal, Column extends string = string, Membership extends ResolverMembership = ResolverMembership> {
|
|
621
638
|
/**
|
|
622
639
|
* The principal's level on each of `ids`, as a map or a record by id. An id
|
|
623
640
|
* left out, or given anything but an access level, has no level. Read
|
|
@@ -631,21 +648,52 @@ interface ResolverOptions<P extends Principal = Principal> {
|
|
|
631
648
|
* no row.
|
|
632
649
|
*/
|
|
633
650
|
readonly where?: (principal: P, level: AccessLevel, tools: PolicyTools) => MaybePromise<AccessFilter>;
|
|
651
|
+
/**
|
|
652
|
+
* What the levels depend on, so tracked writes re-check what they
|
|
653
|
+
* decided: live rows, collection scopes and change topics.
|
|
654
|
+
* `{ columns, memberships }` names the service's columns and the membership
|
|
655
|
+
* tables `levelsFor` and `where` read; `"none"` says nothing a tracked
|
|
656
|
+
* write changes can change a level (the levels come from the principal's
|
|
657
|
+
* grants alone, say). Without it nothing a write does re-checks this
|
|
658
|
+
* policy, and the server says so in development (`resolver-without-reads`).
|
|
659
|
+
*/
|
|
660
|
+
readonly reads?: ResolverReads<Column, Membership> | "none";
|
|
634
661
|
}
|
|
662
|
+
/** The other models and columns the membership tables `M` read: what `defineService` checks. */
|
|
663
|
+
type MembershipColumns<M> = M extends {
|
|
664
|
+
readonly model: infer Model extends string;
|
|
665
|
+
readonly entry: infer Entry extends string;
|
|
666
|
+
readonly user: infer User extends string;
|
|
667
|
+
readonly level: infer Level extends string;
|
|
668
|
+
} ? {
|
|
669
|
+
readonly model: Model;
|
|
670
|
+
readonly columns: Entry | User | Level;
|
|
671
|
+
} : never;
|
|
635
672
|
/**
|
|
636
673
|
* A policy written in app code: `levelsFor` answers the level per row and
|
|
637
674
|
* `where` the list filter. A resolver's own results are memoized for the
|
|
638
|
-
* request and never cached across requests
|
|
639
|
-
*
|
|
640
|
-
*
|
|
675
|
+
* request and never cached across requests; what it reads through `tools`
|
|
676
|
+
* is: `tools.rows(ids)` reads `id` and the declared columns in one query,
|
|
677
|
+
* and `tools.memberships(table, userId, ids)` a member's levels, both kept
|
|
678
|
+
* with `cacheMs` and evicted by tracked writes when `reads` declares them.
|
|
679
|
+
*
|
|
680
|
+
* `reads` declares what the levels depend on, so a tracked write to it
|
|
681
|
+
* re-checks the live rows, collection scopes and change topics the policy
|
|
682
|
+
* authorized, as for the other builders: a user removed from a membership
|
|
683
|
+
* table stops receiving the rows it gave them. `defineService` checks the
|
|
684
|
+
* declared columns and tables against the app's database client.
|
|
641
685
|
*
|
|
642
686
|
* @example
|
|
643
687
|
* access: resolver({
|
|
644
|
-
* levelsFor:
|
|
688
|
+
* levelsFor: (principal, ids, tools) => levelsFromMyTables(principal.userId, ids, tools),
|
|
645
689
|
* where: (principal) => ({ visibility: "public" }),
|
|
690
|
+
* reads: {
|
|
691
|
+
* columns: ["visibility"],
|
|
692
|
+
* memberships: [{ model: "teamMember", entry: "projectId", user: "userId", level: "role" }],
|
|
693
|
+
* },
|
|
646
694
|
* }),
|
|
647
695
|
*/
|
|
648
|
-
declare function resolver<P extends Principal = Principal>(options: ResolverOptions<P>): AccessPolicy<
|
|
696
|
+
declare function resolver<P extends Principal = Principal, const Column extends string = never, const Membership extends ResolverMembership = never>(options: ResolverOptions<P, Column, Membership>): AccessPolicy<Column, MembershipColumns<Membership>>;
|
|
649
697
|
|
|
650
698
|
/**
|
|
651
699
|
* Every signed-in user has `level` on every row. Anonymous callers have no
|
|
@@ -1184,6 +1232,18 @@ interface AdminHandlersOptions<C extends AnyContract, A, Db = unknown> {
|
|
|
1184
1232
|
* an entity with such a field. Default `false`.
|
|
1185
1233
|
*/
|
|
1186
1234
|
readonly grants?: [Extract<AdminFieldOf<C>, "serviceAccess" | "service_access">] extends [never] ? never : boolean;
|
|
1235
|
+
/**
|
|
1236
|
+
* The only fields `adminCreate` and `adminUpdate` write (finding R1.4 of
|
|
1237
|
+
* the 5.0.0 review): `adminMeta` reports every other field `editable:
|
|
1238
|
+
* false`, so a generic form leaves it read-only, and a write naming one is
|
|
1239
|
+
* refused with `VALIDATION` ("is not editable"). Each must be a field the
|
|
1240
|
+
* kit shows, never `id` or a timestamp, and a `fieldOverrides` entry may
|
|
1241
|
+
* not say otherwise about it. When `admin.contract` was given a list, this
|
|
1242
|
+
* one must name the same fields, and without this one the contract's
|
|
1243
|
+
* applies. Without either, the kit writes every field but `id`, the
|
|
1244
|
+
* timestamps, the hidden ones and those an override made read-only.
|
|
1245
|
+
*/
|
|
1246
|
+
readonly editable?: readonly Exclude<AdminFieldOf<C>, AdminNeverWritable>[];
|
|
1187
1247
|
/** Changes to the configuration `adminMeta` derives, per field. */
|
|
1188
1248
|
readonly fieldOverrides?: {
|
|
1189
1249
|
readonly [Field in AdminFieldOf<C>]?: AdminFieldOverride;
|
|
@@ -1253,7 +1313,7 @@ type Empty = Record<never, never>;
|
|
|
1253
1313
|
declare function handlers<C extends AnyContract, const A extends AdminAccess<C> = Empty, Db = unknown>(contract: C & NoInfer<AdminContract<C>>, options?: AdminHandlersOptions<C, A, Db>): AdminImplementations<C, A, Db>;
|
|
1254
1314
|
/**
|
|
1255
1315
|
* The admin kit: `admin.handlers(contract, { access?, displayName?,
|
|
1256
|
-
* hiddenFields?, fieldOverrides?, rowless?, grants? })` implements the
|
|
1316
|
+
* hiddenFields?, editable?, fieldOverrides?, rowless?, grants? })` implements the
|
|
1257
1317
|
* methods `admin.contract` made in `contract`, each open to a service-wide
|
|
1258
1318
|
* `Admin` grant unless `access` gives another form. `admin.contract` is here
|
|
1259
1319
|
* too, for server code; a shared package imports it from the root export.
|
|
@@ -1273,6 +1333,12 @@ declare const admin: Readonly<{
|
|
|
1273
1333
|
readonly sort: `admin.contract: sort: "${Exclude<O extends infer T_3 ? T_3 extends O ? T_3 extends {
|
|
1274
1334
|
readonly sort: readonly (infer Field extends string)[];
|
|
1275
1335
|
} ? Field : never : never : never, ScalarFieldOf<Entity extends StandardSchemaV1<unknown, unknown> ? InferOutput<Entity> : never>> & string}" is not a field of the entity holding strings, numbers or booleans`;
|
|
1336
|
+
}) & ([Exclude<O extends infer T_4 ? T_4 extends O ? T_4 extends {
|
|
1337
|
+
readonly editable: readonly (infer Field extends string)[];
|
|
1338
|
+
} ? Field : never : never : never, Exclude<keyof (Entity extends StandardSchemaV1<unknown, unknown> ? InferOutput<Entity> : never), AdminNeverWritable>>] extends [never] ? unknown : {
|
|
1339
|
+
readonly editable: `admin.contract: editable: "${Exclude<O extends infer T_5 ? T_5 extends O ? T_5 extends {
|
|
1340
|
+
readonly editable: readonly (infer Field extends string)[];
|
|
1341
|
+
} ? Field : never : never : never, Exclude<keyof (Entity extends StandardSchemaV1<unknown, unknown> ? InferOutput<Entity> : never), AdminNeverWritable>> & string}" is not a field of the entity the kit may write`;
|
|
1276
1342
|
}) : unknown>) => AdminMethods<O>;
|
|
1277
1343
|
handlers: typeof handlers;
|
|
1278
1344
|
}>;
|
|
@@ -1491,4 +1557,4 @@ declare function decryptIfEncrypted(value: string): string;
|
|
|
1491
1557
|
/** Safe wrapper that returns `null` instead of throwing on invalid/stale ciphertext. */
|
|
1492
1558
|
declare function tryDecrypt(ciphertext: string, logger?: Logger): string | null;
|
|
1493
1559
|
|
|
1494
|
-
export { ADMIN_HIDDEN_FIELDS, AccessEngine, AccessFilter, AccessFor, type AccessMap, AccessPolicy, type AdminAccess, type AdminContract, type AdminDefaultAccess, type AdminFieldOf, type AdminFieldOverride, type AdminHandlersOptions, type AdminImplementations, type AdminOnCommitted, type AdminOnWrite, type AdminOutputOf, type AdminWrite, type AffectsOption, AnyAccessPolicy, BaseContext, type BasicAccessEngineOptions, CHANNEL_ABUSE_MULTIPLIER, CHANNEL_ABUSE_WINDOW_MS, CallerFor, ChannelOptions, ChannelsRequired, type CollectionOption, type CollectionOptions, type CollectionsRequired, ContextExtensionOf, type ContextFactory, type CrudAccess, type CrudCreateOf, type CrudHandlersOptions, type CrudImplementations, type CrudKindOf, type CrudMethodsOf, type CrudPrepare, CustomAccess, DbOf, type DefineService, Dispatcher, DispatcherCollections, DispatcherOptions, type EnvValidationResult, HandlerArgs, HandlerContext, type HandlerOutputOf, type HandlerRow, INDEX_MAX_ROWS, type InheritOptions, type InitArgs, type InitOptions, type JsonAclOptions, type JsonColumnValue, type KitContext, type KitHandler, type KitHandlerArgs, type LegacyReply, MAX_APP_ROOMS, MAX_ITEM_IDS, MAX_STREAMS_PER_SOCKET, MaybePromise, McpContextOf, type MembersOptions, type MethodAccess, type MethodImplementation, ModelColumn, ModelName, type NextOrdinalOptions, ORDINAL_STEP, PRESENCE_MAX_LAST_SEEN, PolicyFor, PolicyTools, Presence, Principal, type PrincipalFor, PrincipalOf, type ProjectCheck, type ProjectionOption, PublicAccess, type Quickdraw, QuickdrawServer, QuickdrawTypes, RESUME_MAX_AGE_MS, RESUME_MAX_DELTAS, type RedisAdapterOptions, type RedisAdapterResult, type ResolverOptions, RoomLeaveHandler, RowAccess, type RowFor, RowForms, type RowFormsOf, RunContext, RunOptions, STREAM_MAX_SCOPES, type SearchAccess, type SearchContract, type SearchHandlersOptions, type SearchIdsOptions, type SearchImplementations, type SearchMethodsOf, type SearchStrategy, type SearchStrategyContext, ServerOptions, ServerRooms, Service, type ServiceDefinition, ShareMode, type SharingAccess, type SharingByNameOf, type SharingChange, type SharingChangeKind, type SharingContract, type SharingDefaultAccess, type SharingHandlersOptions, type SharingImplementations, type SharingKindOf, type SharingMethodsOf, type SharingOnChange, type SharingOptionsArgs, type SharingResolveUser, type SharingUserLookup, StorageWhere, StreamHandle, StreamOptions, type ValidateEnvOptions, WatchAccess, admin, anyOf, checkEnv, createBasicAccessEngine, crud, custom, decrypt, decryptIfEncrypted, displayNameOf, encrypt, everyone, inherit, initQuickdraw, isEncrypted, isRedisAdapterAvailable, jsonAcl, labelOf, meetsLevel, members, nextOrdinal, owner, requireEnv, requireRow, resolver, search, serviceGrant, setupRedisAdapter, sharing, tryDecrypt, validateEnv };
|
|
1560
|
+
export { ADMIN_HIDDEN_FIELDS, AccessEngine, AccessFilter, AccessFor, type AccessMap, AccessPolicy, type AdminAccess, type AdminContract, type AdminDefaultAccess, type AdminFieldOf, type AdminFieldOverride, type AdminHandlersOptions, type AdminImplementations, type AdminOnCommitted, type AdminOnWrite, type AdminOutputOf, type AdminWrite, type AffectsOption, AnyAccessPolicy, BaseContext, type BasicAccessEngineOptions, CHANNEL_ABUSE_MULTIPLIER, CHANNEL_ABUSE_WINDOW_MS, CallerFor, ChannelOptions, ChannelsRequired, type CollectionOption, type CollectionOptions, type CollectionsRequired, ContextExtensionOf, type ContextFactory, type CrudAccess, type CrudCreateOf, type CrudHandlersOptions, type CrudImplementations, type CrudKindOf, type CrudMethodsOf, type CrudPrepare, CustomAccess, DbOf, type DefineService, Dispatcher, DispatcherCollections, DispatcherOptions, type EnvValidationResult, HandlerArgs, HandlerContext, type HandlerOutputOf, type HandlerRow, INDEX_MAX_ROWS, type InheritOptions, type InitArgs, type InitOptions, type JsonAclOptions, type JsonColumnValue, type KitContext, type KitHandler, type KitHandlerArgs, type LegacyReply, MAX_APP_ROOMS, MAX_ITEM_IDS, MAX_STREAMS_PER_SOCKET, MaybePromise, McpContextOf, type MembersOptions, type MethodAccess, type MethodImplementation, ModelColumn, ModelName, type NextOrdinalOptions, ORDINAL_STEP, PRESENCE_MAX_LAST_SEEN, PolicyFor, PolicyTools, Presence, Principal, type PrincipalFor, PrincipalOf, type ProjectCheck, type ProjectionOption, PublicAccess, type Quickdraw, QuickdrawServer, QuickdrawTypes, RESUME_MAX_AGE_MS, RESUME_MAX_DELTAS, type RedisAdapterOptions, type RedisAdapterResult, type ResolverMembership, type ResolverOptions, type ResolverReads, RoomLeaveHandler, RowAccess, type RowFor, RowForms, type RowFormsOf, RunContext, RunOptions, STREAM_MAX_SCOPES, type SearchAccess, type SearchContract, type SearchHandlersOptions, type SearchIdsOptions, type SearchImplementations, type SearchMethodsOf, type SearchStrategy, type SearchStrategyContext, ServerOptions, ServerRooms, Service, type ServiceDefinition, ShareMode, type SharingAccess, type SharingByNameOf, type SharingChange, type SharingChangeKind, type SharingContract, type SharingDefaultAccess, type SharingHandlersOptions, type SharingImplementations, type SharingKindOf, type SharingMethodsOf, type SharingOnChange, type SharingOptionsArgs, type SharingResolveUser, type SharingUserLookup, StorageWhere, StreamHandle, StreamOptions, type ValidateEnvOptions, WatchAccess, admin, anyOf, checkEnv, createBasicAccessEngine, crud, custom, decrypt, decryptIfEncrypted, displayNameOf, encrypt, everyone, inherit, initQuickdraw, isEncrypted, isRedisAdapterAvailable, jsonAcl, labelOf, meetsLevel, members, nextOrdinal, owner, requireEnv, requireRow, resolver, search, serviceGrant, setupRedisAdapter, sharing, tryDecrypt, validateEnv };
|
package/dist/server/index.js
CHANGED
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
accessFormProblem,
|
|
21
21
|
afterCursor,
|
|
22
22
|
allowedScopes,
|
|
23
|
+
anyOf,
|
|
24
|
+
anyOfPolicies,
|
|
23
25
|
applyRateLimitMiddleware,
|
|
24
26
|
checkName,
|
|
25
27
|
checkWhenDefined,
|
|
@@ -36,7 +38,6 @@ import {
|
|
|
36
38
|
definePolicy,
|
|
37
39
|
encodeCursor,
|
|
38
40
|
handlerProblem,
|
|
39
|
-
idFilter,
|
|
40
41
|
inputKeys,
|
|
41
42
|
isAccessPolicy,
|
|
42
43
|
isCustomAccess,
|
|
@@ -47,16 +48,19 @@ import {
|
|
|
47
48
|
markGrantsEditor,
|
|
48
49
|
maxLevel,
|
|
49
50
|
meetsLevel,
|
|
51
|
+
members,
|
|
50
52
|
membersWhere,
|
|
53
|
+
membershipOf,
|
|
51
54
|
orderByOf,
|
|
52
55
|
projectRow,
|
|
53
56
|
projectedOutput,
|
|
54
57
|
registerRuntime,
|
|
58
|
+
resolver,
|
|
55
59
|
selectWith,
|
|
56
60
|
serviceGrant,
|
|
57
61
|
strip,
|
|
58
62
|
watchAdapterClients
|
|
59
|
-
} from "../chunk-
|
|
63
|
+
} from "../chunk-NS5PUHIZ.js";
|
|
60
64
|
import {
|
|
61
65
|
DevWarningError,
|
|
62
66
|
N_PLUS_ONE_STATEMENTS,
|
|
@@ -64,13 +68,13 @@ import {
|
|
|
64
68
|
currentRev,
|
|
65
69
|
formatDevWarning,
|
|
66
70
|
runBeforeAnyDispatcher
|
|
67
|
-
} from "../chunk-
|
|
71
|
+
} from "../chunk-4DIQHASK.js";
|
|
68
72
|
import {
|
|
69
73
|
crud,
|
|
70
74
|
crudSpecOf,
|
|
71
75
|
sharing,
|
|
72
76
|
sharingSpecOf
|
|
73
|
-
} from "../chunk-
|
|
77
|
+
} from "../chunk-3JNMVB2V.js";
|
|
74
78
|
import "../chunk-QEKVYG5F.js";
|
|
75
79
|
import {
|
|
76
80
|
abortError,
|
|
@@ -98,13 +102,13 @@ import {
|
|
|
98
102
|
adminSpecOf,
|
|
99
103
|
search,
|
|
100
104
|
searchSpecOf
|
|
101
|
-
} from "../chunk-
|
|
105
|
+
} from "../chunk-CDKG4TFA.js";
|
|
102
106
|
import {
|
|
103
107
|
ACCESS_LEVELS,
|
|
104
108
|
entityRoom,
|
|
105
109
|
isAccessLevel,
|
|
106
110
|
reservedRoomPrefix
|
|
107
|
-
} from "../chunk-
|
|
111
|
+
} from "../chunk-WVIRXYI4.js";
|
|
108
112
|
import {
|
|
109
113
|
CHANNEL_DEFAULT_RATE,
|
|
110
114
|
DEFAULT_COLLECTION_LIMIT,
|
|
@@ -1084,60 +1088,6 @@ function jsonAcl(field, options = {}) {
|
|
|
1084
1088
|
return policy;
|
|
1085
1089
|
}
|
|
1086
1090
|
|
|
1087
|
-
// src/server/access/policies/members.ts
|
|
1088
|
-
function membershipOf(policy) {
|
|
1089
|
-
return policy.kind === "members" ? policy.reads.memberships[0] : void 0;
|
|
1090
|
-
}
|
|
1091
|
-
function checkLevels(levels) {
|
|
1092
|
-
if (levels === void 0) {
|
|
1093
|
-
return void 0;
|
|
1094
|
-
}
|
|
1095
|
-
const valid = typeof levels === "object" && levels !== null && !Array.isArray(levels) && Object.values(levels).every(isAccessLevel);
|
|
1096
|
-
if (!valid) {
|
|
1097
|
-
throw new TypeError(
|
|
1098
|
-
'members({ levels }): levels must map stored roles to access levels, as in { editor: "Moderate" }'
|
|
1099
|
-
);
|
|
1100
|
-
}
|
|
1101
|
-
return Object.freeze({ ...levels });
|
|
1102
|
-
}
|
|
1103
|
-
function rolesAtLeast(read, level) {
|
|
1104
|
-
const enough = new Set(levelsAtLeast(level));
|
|
1105
|
-
if (read.levels === void 0) {
|
|
1106
|
-
return [...enough];
|
|
1107
|
-
}
|
|
1108
|
-
return Object.entries(read.levels).filter(([, mapped]) => enough.has(mapped)).map(([role]) => role);
|
|
1109
|
-
}
|
|
1110
|
-
function members(options) {
|
|
1111
|
-
if (typeof options !== "object" || options === null) {
|
|
1112
|
-
throw new TypeError("members(options): options must be { model, entry, user, level }");
|
|
1113
|
-
}
|
|
1114
|
-
const read = Object.freeze({
|
|
1115
|
-
model: checkName("members({ model })", "model", options.model),
|
|
1116
|
-
entry: checkName("members({ entry })", "entry", options.entry),
|
|
1117
|
-
user: checkName("members({ user })", "user", options.user),
|
|
1118
|
-
level: checkName("members({ level })", "level", options.level),
|
|
1119
|
-
levels: checkLevels(options.levels)
|
|
1120
|
-
});
|
|
1121
|
-
return definePolicy({
|
|
1122
|
-
kind: "members",
|
|
1123
|
-
membership: read,
|
|
1124
|
-
reads: { columns: [], memberships: [read], inherits: [], storage: true },
|
|
1125
|
-
levelsFor: (principal, ids, tools) => tools.memberships(read, principal.userId, ids),
|
|
1126
|
-
async accessWhere(principal, level, tools) {
|
|
1127
|
-
const roles = rolesAtLeast(read, level);
|
|
1128
|
-
if (roles.length === 0) {
|
|
1129
|
-
return "none";
|
|
1130
|
-
}
|
|
1131
|
-
const rows = await tools.storage.findMany(read.model, {
|
|
1132
|
-
where: { [read.user]: principal.userId, [read.level]: { in: roles } },
|
|
1133
|
-
select: { [read.entry]: true }
|
|
1134
|
-
});
|
|
1135
|
-
const entries = rows.map((row) => row[read.entry]);
|
|
1136
|
-
return idFilter(entries.filter((entry) => typeof entry === "string"));
|
|
1137
|
-
}
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
1091
|
// src/server/access/policies/inherit.ts
|
|
1142
1092
|
function isContract3(value) {
|
|
1143
1093
|
return typeof value === "object" && value !== null && typeof value.name === "string" && Object.isFrozen(value);
|
|
@@ -1185,101 +1135,6 @@ function inherit(options) {
|
|
|
1185
1135
|
});
|
|
1186
1136
|
}
|
|
1187
1137
|
|
|
1188
|
-
// src/server/access/policies/anyOf.ts
|
|
1189
|
-
function unionReads(policies) {
|
|
1190
|
-
const memberships = /* @__PURE__ */ new Set();
|
|
1191
|
-
for (const policy of policies) {
|
|
1192
|
-
for (const read of policy.reads.memberships) {
|
|
1193
|
-
memberships.add(read);
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
return {
|
|
1197
|
-
columns: [...new Set(policies.flatMap((policy) => policy.reads.columns))],
|
|
1198
|
-
memberships: [...memberships],
|
|
1199
|
-
inherits: [...new Set(policies.flatMap((policy) => policy.reads.inherits))],
|
|
1200
|
-
parents: policies.flatMap((policy) => policy.reads.parents ?? []),
|
|
1201
|
-
storage: policies.some((policy) => policy.reads.storage)
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
var MEMBERS = /* @__PURE__ */ new WeakMap();
|
|
1205
|
-
function anyOfPolicies(policy) {
|
|
1206
|
-
return MEMBERS.get(policy);
|
|
1207
|
-
}
|
|
1208
|
-
function anyOf(...policies) {
|
|
1209
|
-
if (policies.length === 0 || !policies.every(isAccessPolicy)) {
|
|
1210
|
-
throw new TypeError("anyOf(...policies): pass one or more access policies");
|
|
1211
|
-
}
|
|
1212
|
-
const all = Object.freeze([...policies]);
|
|
1213
|
-
const combined = definePolicy({
|
|
1214
|
-
kind: "anyOf",
|
|
1215
|
-
policies: all,
|
|
1216
|
-
reads: unionReads(all),
|
|
1217
|
-
async levelsFor(principal, ids, tools) {
|
|
1218
|
-
const each = await Promise.all(all.map((policy) => policy.levelsFor(principal, ids, tools)));
|
|
1219
|
-
const levels = /* @__PURE__ */ new Map();
|
|
1220
|
-
for (const id of ids) {
|
|
1221
|
-
levels.set(
|
|
1222
|
-
id,
|
|
1223
|
-
each.reduce((level, granted) => maxLevel(level, granted.get(id)), null)
|
|
1224
|
-
);
|
|
1225
|
-
}
|
|
1226
|
-
return levels;
|
|
1227
|
-
},
|
|
1228
|
-
async accessWhere(principal, level, tools) {
|
|
1229
|
-
const filters = await Promise.all(
|
|
1230
|
-
all.map((policy) => policy.accessWhere(principal, level, tools))
|
|
1231
|
-
);
|
|
1232
|
-
const matching = filters.filter((filter) => filter !== "none");
|
|
1233
|
-
if (matching.some((filter) => Object.keys(filter).length === 0)) {
|
|
1234
|
-
return {};
|
|
1235
|
-
}
|
|
1236
|
-
if (matching.length <= 1) {
|
|
1237
|
-
return matching[0] ?? "none";
|
|
1238
|
-
}
|
|
1239
|
-
return { OR: matching };
|
|
1240
|
-
}
|
|
1241
|
-
});
|
|
1242
|
-
MEMBERS.set(combined, all);
|
|
1243
|
-
return combined;
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
// src/server/access/policies/resolver.ts
|
|
1247
|
-
function levelIn(levels, id) {
|
|
1248
|
-
let level;
|
|
1249
|
-
if (levels instanceof Map) {
|
|
1250
|
-
level = levels.get(id);
|
|
1251
|
-
} else if (Object.hasOwn(levels, id)) {
|
|
1252
|
-
level = levels[id];
|
|
1253
|
-
}
|
|
1254
|
-
return isAccessLevel(level) ? level : null;
|
|
1255
|
-
}
|
|
1256
|
-
function resolver(options) {
|
|
1257
|
-
const valid = typeof options === "object" && options !== null && typeof options.levelsFor === "function" && (options.where === void 0 || typeof options.where === "function");
|
|
1258
|
-
if (!valid) {
|
|
1259
|
-
throw new TypeError("resolver({ levelsFor, where? }): levelsFor and where must be functions");
|
|
1260
|
-
}
|
|
1261
|
-
const { levelsFor, where } = options;
|
|
1262
|
-
return definePolicy({
|
|
1263
|
-
kind: "resolver",
|
|
1264
|
-
reads: { columns: [], memberships: [], inherits: [], storage: false },
|
|
1265
|
-
async levelsFor(principal, ids, tools) {
|
|
1266
|
-
const levels = await levelsFor(principal, ids, tools);
|
|
1267
|
-
if (typeof levels !== "object" || levels === null) {
|
|
1268
|
-
return levelsById(ids, () => null);
|
|
1269
|
-
}
|
|
1270
|
-
return levelsById(ids, (id) => levelIn(levels, id));
|
|
1271
|
-
},
|
|
1272
|
-
async accessWhere(principal, level, tools) {
|
|
1273
|
-
if (where === void 0) {
|
|
1274
|
-
return "none";
|
|
1275
|
-
}
|
|
1276
|
-
const filter = await where(principal, level, tools);
|
|
1277
|
-
const isFilter = typeof filter === "object" && filter !== null && !Array.isArray(filter);
|
|
1278
|
-
return isFilter ? filter : "none";
|
|
1279
|
-
}
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
1138
|
// src/server/access/policies/everyone.ts
|
|
1284
1139
|
function everyone(level) {
|
|
1285
1140
|
if (!isAccessLevel(level) || level === "Public") {
|
|
@@ -2483,7 +2338,7 @@ function parseList(value) {
|
|
|
2483
2338
|
}
|
|
2484
2339
|
return Array.isArray(value) && value.every(isEntry) ? value : void 0;
|
|
2485
2340
|
}
|
|
2486
|
-
function
|
|
2341
|
+
function levelIn(entries, userId) {
|
|
2487
2342
|
return entries.filter((entry) => entry.userId === userId).reduce((level, entry) => maxLevel(level, entry.level), null);
|
|
2488
2343
|
}
|
|
2489
2344
|
function entriesOf(entries, userId) {
|
|
@@ -2792,7 +2647,7 @@ function refusal2(listed, change, model) {
|
|
|
2792
2647
|
`That user owns this ${model}: an owner's access cannot be shared, changed or taken away`
|
|
2793
2648
|
);
|
|
2794
2649
|
}
|
|
2795
|
-
if (change.listed &&
|
|
2650
|
+
if (change.listed && levelIn(listed.entries, change.userId) === null) {
|
|
2796
2651
|
return new QuickdrawError("NOT_FOUND", `This ${model} is not shared with that user`);
|
|
2797
2652
|
}
|
|
2798
2653
|
return void 0;
|
|
@@ -2819,7 +2674,7 @@ async function changeList(call, context, change) {
|
|
|
2819
2674
|
throw problem;
|
|
2820
2675
|
}
|
|
2821
2676
|
await checkLastAdmin(call, tx, listed, change);
|
|
2822
|
-
const before =
|
|
2677
|
+
const before = levelIn(listed.entries, change.userId);
|
|
2823
2678
|
if (before === change.level && entriesOf(listed.entries, change.userId) === 1) {
|
|
2824
2679
|
return sharesOf(listed.entries);
|
|
2825
2680
|
}
|
|
@@ -3335,6 +3190,53 @@ function checkOverrides(fieldOverrides, names, hidden, fail5) {
|
|
|
3335
3190
|
}
|
|
3336
3191
|
return fieldOverrides;
|
|
3337
3192
|
}
|
|
3193
|
+
function quoted(names) {
|
|
3194
|
+
return names.map((name) => `"${name}"`).join(", ");
|
|
3195
|
+
}
|
|
3196
|
+
function checkEditable(editable, declared, shown, fail5) {
|
|
3197
|
+
const named = (label, list2) => {
|
|
3198
|
+
for (const name of list2) {
|
|
3199
|
+
if (ADMIN_NEVER_WRITABLE.includes(name)) {
|
|
3200
|
+
fail5(`${label}: "${name}" is never editable; the database sets it`);
|
|
3201
|
+
}
|
|
3202
|
+
if (!shown(name)) {
|
|
3203
|
+
fail5(`${label}: "${name}" is not a field of the entity the kit shows`);
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
return new Set(list2);
|
|
3207
|
+
};
|
|
3208
|
+
if (editable === void 0) {
|
|
3209
|
+
return declared === void 0 ? void 0 : named("admin.contract's editable", declared);
|
|
3210
|
+
}
|
|
3211
|
+
const valid = Array.isArray(editable) && editable.every((name) => typeof name === "string") && new Set(editable).size === editable.length;
|
|
3212
|
+
if (!valid) {
|
|
3213
|
+
fail5("editable must be a list of distinct field names");
|
|
3214
|
+
}
|
|
3215
|
+
const list = named("editable", editable);
|
|
3216
|
+
if (declared !== void 0 && (declared.length !== list.size || !declared.every((name) => list.has(name)))) {
|
|
3217
|
+
fail5(
|
|
3218
|
+
`editable names ${quoted(editable)}, but admin.contract was given ${quoted(declared)}: give both halves the same fields`
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
return list;
|
|
3222
|
+
}
|
|
3223
|
+
function checkAgreement(overrides, editable, fail5) {
|
|
3224
|
+
if (editable === void 0) {
|
|
3225
|
+
return;
|
|
3226
|
+
}
|
|
3227
|
+
for (const [name, override] of Object.entries(overrides)) {
|
|
3228
|
+
if (override.editable === true && !editable.has(name)) {
|
|
3229
|
+
fail5(
|
|
3230
|
+
`fieldOverrides: "${name}" is editable: true, which editable leaves out; name it in editable instead`
|
|
3231
|
+
);
|
|
3232
|
+
}
|
|
3233
|
+
if (override.editable === false && editable.has(name)) {
|
|
3234
|
+
fail5(
|
|
3235
|
+
`fieldOverrides: "${name}" is editable: false, which editable names; leave it out of editable instead`
|
|
3236
|
+
);
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3338
3240
|
function derived(field, grants) {
|
|
3339
3241
|
const fixed = Object.hasOwn(DEFAULT_FIELDS, field.name) ? DEFAULT_FIELDS[field.name] : void 0;
|
|
3340
3242
|
if (fixed !== void 0) {
|
|
@@ -3372,10 +3274,18 @@ function adminFieldsOf(serviceName, spec, options, fail5) {
|
|
|
3372
3274
|
fail5(`"${listed}" is hidden, so adminList may not filter or sort on it`);
|
|
3373
3275
|
}
|
|
3374
3276
|
const overrides = checkOverrides(options.fieldOverrides, names, hidden, fail5);
|
|
3277
|
+
const editable = checkEditable(
|
|
3278
|
+
options.editable,
|
|
3279
|
+
spec.editable,
|
|
3280
|
+
(name) => names.includes(name) && !hidden.has(name),
|
|
3281
|
+
fail5
|
|
3282
|
+
);
|
|
3283
|
+
checkAgreement(overrides, editable, fail5);
|
|
3375
3284
|
const fields = ordered(spec.fields).filter((field) => !hidden.has(field.name)).map(
|
|
3376
3285
|
(field) => frozenConfig({
|
|
3377
3286
|
...derived(field, grants),
|
|
3378
3287
|
...overrides[field.name],
|
|
3288
|
+
...editable === void 0 ? {} : { editable: editable.has(field.name) },
|
|
3379
3289
|
sortable: spec.sort.includes(field.name),
|
|
3380
3290
|
filterable: spec.filter.includes(field.name)
|
|
3381
3291
|
})
|
|
@@ -3701,6 +3611,7 @@ var OPTION_KEYS4 = [
|
|
|
3701
3611
|
"access",
|
|
3702
3612
|
"displayName",
|
|
3703
3613
|
"hiddenFields",
|
|
3614
|
+
"editable",
|
|
3704
3615
|
"fieldOverrides",
|
|
3705
3616
|
"rowless",
|
|
3706
3617
|
"grants",
|
|
@@ -3741,7 +3652,7 @@ function checkOptions3(options) {
|
|
|
3741
3652
|
}
|
|
3742
3653
|
if (!isRecord11(options)) {
|
|
3743
3654
|
fail4(
|
|
3744
|
-
"options must be { access?, displayName?, hiddenFields?, fieldOverrides?, rowless?, grants?, onWrite?, onCommitted? }"
|
|
3655
|
+
"options must be { access?, displayName?, hiddenFields?, editable?, fieldOverrides?, rowless?, grants?, onWrite?, onCommitted? }"
|
|
3745
3656
|
);
|
|
3746
3657
|
}
|
|
3747
3658
|
const unknownKey = Object.keys(options).find((key) => !OPTION_KEYS4.includes(key));
|
|
@@ -3768,13 +3679,28 @@ function checkAccess3(access, names) {
|
|
|
3768
3679
|
}
|
|
3769
3680
|
return access;
|
|
3770
3681
|
}
|
|
3682
|
+
function contractEditable(contract, specs) {
|
|
3683
|
+
const lists = /* @__PURE__ */ new Map();
|
|
3684
|
+
for (const { editable } of specs) {
|
|
3685
|
+
if (editable !== void 0) {
|
|
3686
|
+
lists.set([...editable].sort().join("\0"), editable);
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
if (lists.size > 1) {
|
|
3690
|
+
fail4(
|
|
3691
|
+
`admin.contract was given two editable lists for ${contract.name}: give its write methods one`
|
|
3692
|
+
);
|
|
3693
|
+
}
|
|
3694
|
+
return [...lists.values()][0];
|
|
3695
|
+
}
|
|
3771
3696
|
function fieldsOf(contract, kit, options) {
|
|
3772
3697
|
const specs = kit.map(([, spec2]) => spec2);
|
|
3773
3698
|
const [first] = specs;
|
|
3774
3699
|
const spec = {
|
|
3775
3700
|
fields: first?.fields ?? [],
|
|
3776
3701
|
filter: [...new Set(specs.flatMap((one) => one.filter))],
|
|
3777
|
-
sort: [...new Set(specs.flatMap((one) => one.sort))]
|
|
3702
|
+
sort: [...new Set(specs.flatMap((one) => one.sort))],
|
|
3703
|
+
editable: contractEditable(contract, specs)
|
|
3778
3704
|
};
|
|
3779
3705
|
return adminFieldsOf(
|
|
3780
3706
|
contract.name,
|
|
@@ -3782,6 +3708,7 @@ function fieldsOf(contract, kit, options) {
|
|
|
3782
3708
|
{
|
|
3783
3709
|
displayName: options.displayName,
|
|
3784
3710
|
hiddenFields: options.hiddenFields,
|
|
3711
|
+
editable: options.editable,
|
|
3785
3712
|
fieldOverrides: options.fieldOverrides,
|
|
3786
3713
|
grants: options.grants
|
|
3787
3714
|
},
|