@objectstack/plugin-security 9.10.0 → 9.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.
package/dist/index.d.mts CHANGED
@@ -89,6 +89,8 @@ declare class SecurityPlugin implements Plugin {
89
89
  */
90
90
  private metadata;
91
91
  private ql;
92
+ /** ADR-0055: cache the resolved master-detail relation per controlled_by_parent object. */
93
+ private cbpRelCache;
92
94
  private dbLoader?;
93
95
  private logger;
94
96
  constructor(options?: SecurityPluginOptions);
@@ -143,6 +145,45 @@ declare class SecurityPlugin implements Plugin {
143
145
  * applies (caller adds no filter).
144
146
  */
145
147
  private computeRlsFilter;
148
+ /**
149
+ * Resolve a controlled_by_parent object's master-detail relation (the FK field
150
+ * key + the master object name), or null. Prefers a required `master_detail`
151
+ * field; falls back to any `master_detail`, then a required `lookup`. Cached.
152
+ */
153
+ private resolveCbpRelation;
154
+ /**
155
+ * ADR-0055 — master-detail "controlled by parent" READ derivation.
156
+ *
157
+ * For an object whose `sharingModel` is `controlled_by_parent`, access is
158
+ * derived from the master: return a filter `masterFK IN (<master ids this user
159
+ * can read>)`. The id set is resolved by running the MASTER's own read RLS
160
+ * (reused via `computeRlsFilter`) under a system context — no middleware
161
+ * re-entry, so no recursion. An empty set yields `{ masterFK: { $in: [] } }`,
162
+ * which matches no rows (fail closed). A misconfigured object (no
163
+ * master_detail/lookup to derive from) denies all reads (defense-in-depth;
164
+ * spec validation should prevent authoring it). Returns null when the object is
165
+ * not controlled_by_parent.
166
+ *
167
+ * v1 scope (ADR-0055): single level — the master's OWN controlled_by_parent is
168
+ * not traversed transitively; master accessibility is the master's RLS filter
169
+ * (sharing-service grants on the master are not folded in).
170
+ */
171
+ private computeControlledByParentFilter;
172
+ /**
173
+ * ADR-0055 — master-detail "controlled by parent" WRITE enforcement.
174
+ *
175
+ * A by-id write (insert/update/delete) to a controlled_by_parent detail
176
+ * requires EDIT access to its master: the caller must hold CRUD `update` on the
177
+ * master object AND the master row must be visible under the master's write RLS.
178
+ * This is the write-side companion to the read derivation — the RLS read filter
179
+ * never applies to a by-id write (the #1994 class), so without this a member
180
+ * could mutate a detail under a master they cannot edit. Throws on denial;
181
+ * no-op when the object is not controlled_by_parent.
182
+ *
183
+ * v1 scope: single-id writes. Bulk writes flow through the AST and are already
184
+ * scoped by the controlled-by-parent READ filter (to readable masters).
185
+ */
186
+ private assertControlledByParentWrite;
146
187
  /**
147
188
  * Collect all RLS policies from permission sets applicable to the given object/operation.
148
189
  */
@@ -235,6 +276,14 @@ interface RLSUserContext {
235
276
  * and are merged in under their own keys (see {@link RLSCompiler.compileFilter}).
236
277
  */
237
278
  org_user_ids?: string[];
279
+ /**
280
+ * The caller's unique, auth-enforced email. RLS expressions reference it as
281
+ * `current_user.email` for human-readable, *seedable* owner scoping
282
+ * (`owner = current_user.email`). Email is exposed because it is UNIQUE; the
283
+ * user's display `name` is deliberately NOT exposed — names collide, and a
284
+ * collision on an ownership predicate is an access-control leak.
285
+ */
286
+ email?: string;
238
287
  [key: string]: unknown;
239
288
  }
240
289
  /**
@@ -260,6 +309,11 @@ declare const RLS_DENY_FILTER: Record<string, unknown>;
260
309
  * Converts `using` / `check` expressions into ObjectQL-compatible filter conditions.
261
310
  */
262
311
  declare class RLSCompiler {
312
+ /** Optional logger so a SILENTLY-dropped (uncompilable-shape) policy is observable (ADR-0056 D4). */
313
+ private logger?;
314
+ setLogger(logger: {
315
+ warn?: (message: string, meta?: Record<string, unknown>) => void;
316
+ } | undefined): void;
263
317
  /**
264
318
  * Compile RLS policies into a query filter for the given user context.
265
319
  * Multiple policies for the same object/operation are OR-combined (any match allows access).
@@ -364,7 +418,7 @@ declare const securityObjects: ((Omit<{
364
418
  abstract: boolean;
365
419
  datasource: string;
366
420
  fields: Record<string, {
367
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
421
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
368
422
  required: boolean;
369
423
  searchable: boolean;
370
424
  multiple: boolean;
@@ -985,7 +1039,7 @@ declare const securityObjects: ((Omit<{
985
1039
  clone: boolean;
986
1040
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
987
1041
  } | undefined;
988
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
1042
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
989
1043
  publicSharing?: {
990
1044
  enabled: boolean;
991
1045
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -2709,7 +2763,7 @@ declare const securityObjects: ((Omit<{
2709
2763
  abstract: boolean;
2710
2764
  datasource: string;
2711
2765
  fields: Record<string, {
2712
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
2766
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
2713
2767
  required: boolean;
2714
2768
  searchable: boolean;
2715
2769
  multiple: boolean;
@@ -3330,7 +3384,7 @@ declare const securityObjects: ((Omit<{
3330
3384
  clone: boolean;
3331
3385
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
3332
3386
  } | undefined;
3333
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
3387
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
3334
3388
  publicSharing?: {
3335
3389
  enabled: boolean;
3336
3390
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -5483,7 +5537,7 @@ declare const securityObjects: ((Omit<{
5483
5537
  abstract: boolean;
5484
5538
  datasource: string;
5485
5539
  fields: Record<string, {
5486
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
5540
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
5487
5541
  required: boolean;
5488
5542
  searchable: boolean;
5489
5543
  multiple: boolean;
@@ -6104,7 +6158,7 @@ declare const securityObjects: ((Omit<{
6104
6158
  clone: boolean;
6105
6159
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
6106
6160
  } | undefined;
6107
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
6161
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
6108
6162
  publicSharing?: {
6109
6163
  enabled: boolean;
6110
6164
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -7349,7 +7403,7 @@ declare const securityObjects: ((Omit<{
7349
7403
  abstract: boolean;
7350
7404
  datasource: string;
7351
7405
  fields: Record<string, {
7352
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
7406
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
7353
7407
  required: boolean;
7354
7408
  searchable: boolean;
7355
7409
  multiple: boolean;
@@ -7970,7 +8024,7 @@ declare const securityObjects: ((Omit<{
7970
8024
  clone: boolean;
7971
8025
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
7972
8026
  } | undefined;
7973
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
8027
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
7974
8028
  publicSharing?: {
7975
8029
  enabled: boolean;
7976
8030
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -8901,6 +8955,7 @@ declare const securityObjects: ((Omit<{
8901
8955
  declare const securityDefaultPermissionSets: {
8902
8956
  name: string;
8903
8957
  isProfile: boolean;
8958
+ isDefault: boolean;
8904
8959
  objects: Record<string, {
8905
8960
  allowCreate: boolean;
8906
8961
  allowRead: boolean;
@@ -9043,4 +9098,21 @@ declare function claimSeedOwnership(ql: any, adminUserId: string, options?: Clai
9043
9098
  count: number;
9044
9099
  }[]>;
9045
9100
 
9046
- export { FieldMasker, PermissionDeniedError, PermissionEvaluator, RLSCompiler, RLS_DENY_FILTER, SECURITY_PLUGIN_ID, SECURITY_PLUGIN_VERSION, SecurityPlugin, backfillOrgAdminGrants, bootstrapPlatformAdmin, claimSeedOwnership, isPermissionDeniedError, reconcileOrgAdminGrant, securityDefaultPermissionSets, securityObjects, securityPluginManifestHeader };
9101
+ /**
9102
+ * ADR-0056 D7 — resolve the app-declared default profile NAME from a stack's
9103
+ * `permissions[]` array.
9104
+ *
9105
+ * A permission set marked `isProfile && isDefault` declares the app's default
9106
+ * access posture for users with no explicit grants. The {@link SecurityPlugin}
9107
+ * constructor scans its `defaultPermissionSets` option for that flag — but the
9108
+ * CLI constructs `new SecurityPlugin()` with NO options, so an `isDefault`
9109
+ * profile declared purely in app METADATA would never be honored. The CLI calls
9110
+ * this helper to pull the name out of the stack and pass it as
9111
+ * `fallbackPermissionSet`, wiring the declaration through to `pnpm dev`.
9112
+ *
9113
+ * Returns the first matching profile's `name`, or `undefined` when none is
9114
+ * declared (callers then keep the built-in `member_default` fallback).
9115
+ */
9116
+ declare function appDefaultProfileName(permissions: unknown): string | undefined;
9117
+
9118
+ export { FieldMasker, PermissionDeniedError, PermissionEvaluator, RLSCompiler, RLS_DENY_FILTER, SECURITY_PLUGIN_ID, SECURITY_PLUGIN_VERSION, SecurityPlugin, appDefaultProfileName, backfillOrgAdminGrants, bootstrapPlatformAdmin, claimSeedOwnership, isPermissionDeniedError, reconcileOrgAdminGrant, securityDefaultPermissionSets, securityObjects, securityPluginManifestHeader };
package/dist/index.d.ts CHANGED
@@ -89,6 +89,8 @@ declare class SecurityPlugin implements Plugin {
89
89
  */
90
90
  private metadata;
91
91
  private ql;
92
+ /** ADR-0055: cache the resolved master-detail relation per controlled_by_parent object. */
93
+ private cbpRelCache;
92
94
  private dbLoader?;
93
95
  private logger;
94
96
  constructor(options?: SecurityPluginOptions);
@@ -143,6 +145,45 @@ declare class SecurityPlugin implements Plugin {
143
145
  * applies (caller adds no filter).
144
146
  */
145
147
  private computeRlsFilter;
148
+ /**
149
+ * Resolve a controlled_by_parent object's master-detail relation (the FK field
150
+ * key + the master object name), or null. Prefers a required `master_detail`
151
+ * field; falls back to any `master_detail`, then a required `lookup`. Cached.
152
+ */
153
+ private resolveCbpRelation;
154
+ /**
155
+ * ADR-0055 — master-detail "controlled by parent" READ derivation.
156
+ *
157
+ * For an object whose `sharingModel` is `controlled_by_parent`, access is
158
+ * derived from the master: return a filter `masterFK IN (<master ids this user
159
+ * can read>)`. The id set is resolved by running the MASTER's own read RLS
160
+ * (reused via `computeRlsFilter`) under a system context — no middleware
161
+ * re-entry, so no recursion. An empty set yields `{ masterFK: { $in: [] } }`,
162
+ * which matches no rows (fail closed). A misconfigured object (no
163
+ * master_detail/lookup to derive from) denies all reads (defense-in-depth;
164
+ * spec validation should prevent authoring it). Returns null when the object is
165
+ * not controlled_by_parent.
166
+ *
167
+ * v1 scope (ADR-0055): single level — the master's OWN controlled_by_parent is
168
+ * not traversed transitively; master accessibility is the master's RLS filter
169
+ * (sharing-service grants on the master are not folded in).
170
+ */
171
+ private computeControlledByParentFilter;
172
+ /**
173
+ * ADR-0055 — master-detail "controlled by parent" WRITE enforcement.
174
+ *
175
+ * A by-id write (insert/update/delete) to a controlled_by_parent detail
176
+ * requires EDIT access to its master: the caller must hold CRUD `update` on the
177
+ * master object AND the master row must be visible under the master's write RLS.
178
+ * This is the write-side companion to the read derivation — the RLS read filter
179
+ * never applies to a by-id write (the #1994 class), so without this a member
180
+ * could mutate a detail under a master they cannot edit. Throws on denial;
181
+ * no-op when the object is not controlled_by_parent.
182
+ *
183
+ * v1 scope: single-id writes. Bulk writes flow through the AST and are already
184
+ * scoped by the controlled-by-parent READ filter (to readable masters).
185
+ */
186
+ private assertControlledByParentWrite;
146
187
  /**
147
188
  * Collect all RLS policies from permission sets applicable to the given object/operation.
148
189
  */
@@ -235,6 +276,14 @@ interface RLSUserContext {
235
276
  * and are merged in under their own keys (see {@link RLSCompiler.compileFilter}).
236
277
  */
237
278
  org_user_ids?: string[];
279
+ /**
280
+ * The caller's unique, auth-enforced email. RLS expressions reference it as
281
+ * `current_user.email` for human-readable, *seedable* owner scoping
282
+ * (`owner = current_user.email`). Email is exposed because it is UNIQUE; the
283
+ * user's display `name` is deliberately NOT exposed — names collide, and a
284
+ * collision on an ownership predicate is an access-control leak.
285
+ */
286
+ email?: string;
238
287
  [key: string]: unknown;
239
288
  }
240
289
  /**
@@ -260,6 +309,11 @@ declare const RLS_DENY_FILTER: Record<string, unknown>;
260
309
  * Converts `using` / `check` expressions into ObjectQL-compatible filter conditions.
261
310
  */
262
311
  declare class RLSCompiler {
312
+ /** Optional logger so a SILENTLY-dropped (uncompilable-shape) policy is observable (ADR-0056 D4). */
313
+ private logger?;
314
+ setLogger(logger: {
315
+ warn?: (message: string, meta?: Record<string, unknown>) => void;
316
+ } | undefined): void;
263
317
  /**
264
318
  * Compile RLS policies into a query filter for the given user context.
265
319
  * Multiple policies for the same object/operation are OR-combined (any match allows access).
@@ -364,7 +418,7 @@ declare const securityObjects: ((Omit<{
364
418
  abstract: boolean;
365
419
  datasource: string;
366
420
  fields: Record<string, {
367
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
421
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
368
422
  required: boolean;
369
423
  searchable: boolean;
370
424
  multiple: boolean;
@@ -985,7 +1039,7 @@ declare const securityObjects: ((Omit<{
985
1039
  clone: boolean;
986
1040
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
987
1041
  } | undefined;
988
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
1042
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
989
1043
  publicSharing?: {
990
1044
  enabled: boolean;
991
1045
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -2709,7 +2763,7 @@ declare const securityObjects: ((Omit<{
2709
2763
  abstract: boolean;
2710
2764
  datasource: string;
2711
2765
  fields: Record<string, {
2712
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
2766
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
2713
2767
  required: boolean;
2714
2768
  searchable: boolean;
2715
2769
  multiple: boolean;
@@ -3330,7 +3384,7 @@ declare const securityObjects: ((Omit<{
3330
3384
  clone: boolean;
3331
3385
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
3332
3386
  } | undefined;
3333
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
3387
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
3334
3388
  publicSharing?: {
3335
3389
  enabled: boolean;
3336
3390
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -5483,7 +5537,7 @@ declare const securityObjects: ((Omit<{
5483
5537
  abstract: boolean;
5484
5538
  datasource: string;
5485
5539
  fields: Record<string, {
5486
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
5540
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
5487
5541
  required: boolean;
5488
5542
  searchable: boolean;
5489
5543
  multiple: boolean;
@@ -6104,7 +6158,7 @@ declare const securityObjects: ((Omit<{
6104
6158
  clone: boolean;
6105
6159
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
6106
6160
  } | undefined;
6107
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
6161
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
6108
6162
  publicSharing?: {
6109
6163
  enabled: boolean;
6110
6164
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -7349,7 +7403,7 @@ declare const securityObjects: ((Omit<{
7349
7403
  abstract: boolean;
7350
7404
  datasource: string;
7351
7405
  fields: Record<string, {
7352
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
7406
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
7353
7407
  required: boolean;
7354
7408
  searchable: boolean;
7355
7409
  multiple: boolean;
@@ -7970,7 +8024,7 @@ declare const securityObjects: ((Omit<{
7970
8024
  clone: boolean;
7971
8025
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
7972
8026
  } | undefined;
7973
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
8027
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
7974
8028
  publicSharing?: {
7975
8029
  enabled: boolean;
7976
8030
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -8901,6 +8955,7 @@ declare const securityObjects: ((Omit<{
8901
8955
  declare const securityDefaultPermissionSets: {
8902
8956
  name: string;
8903
8957
  isProfile: boolean;
8958
+ isDefault: boolean;
8904
8959
  objects: Record<string, {
8905
8960
  allowCreate: boolean;
8906
8961
  allowRead: boolean;
@@ -9043,4 +9098,21 @@ declare function claimSeedOwnership(ql: any, adminUserId: string, options?: Clai
9043
9098
  count: number;
9044
9099
  }[]>;
9045
9100
 
9046
- export { FieldMasker, PermissionDeniedError, PermissionEvaluator, RLSCompiler, RLS_DENY_FILTER, SECURITY_PLUGIN_ID, SECURITY_PLUGIN_VERSION, SecurityPlugin, backfillOrgAdminGrants, bootstrapPlatformAdmin, claimSeedOwnership, isPermissionDeniedError, reconcileOrgAdminGrant, securityDefaultPermissionSets, securityObjects, securityPluginManifestHeader };
9101
+ /**
9102
+ * ADR-0056 D7 — resolve the app-declared default profile NAME from a stack's
9103
+ * `permissions[]` array.
9104
+ *
9105
+ * A permission set marked `isProfile && isDefault` declares the app's default
9106
+ * access posture for users with no explicit grants. The {@link SecurityPlugin}
9107
+ * constructor scans its `defaultPermissionSets` option for that flag — but the
9108
+ * CLI constructs `new SecurityPlugin()` with NO options, so an `isDefault`
9109
+ * profile declared purely in app METADATA would never be honored. The CLI calls
9110
+ * this helper to pull the name out of the stack and pass it as
9111
+ * `fallbackPermissionSet`, wiring the declaration through to `pnpm dev`.
9112
+ *
9113
+ * Returns the first matching profile's `name`, or `undefined` when none is
9114
+ * declared (callers then keep the built-in `member_default` fallback).
9115
+ */
9116
+ declare function appDefaultProfileName(permissions: unknown): string | undefined;
9117
+
9118
+ export { FieldMasker, PermissionDeniedError, PermissionEvaluator, RLSCompiler, RLS_DENY_FILTER, SECURITY_PLUGIN_ID, SECURITY_PLUGIN_VERSION, SecurityPlugin, appDefaultProfileName, backfillOrgAdminGrants, bootstrapPlatformAdmin, claimSeedOwnership, isPermissionDeniedError, reconcileOrgAdminGrant, securityDefaultPermissionSets, securityObjects, securityPluginManifestHeader };