@objectstack/platform-objects 12.6.0 → 14.3.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.
Files changed (41) hide show
  1. package/dist/apps/index.d.mts +1 -1
  2. package/dist/apps/index.d.ts +1 -1
  3. package/dist/apps/index.js +38 -17
  4. package/dist/apps/index.js.map +1 -1
  5. package/dist/apps/index.mjs +38 -17
  6. package/dist/apps/index.mjs.map +1 -1
  7. package/dist/audit/index.d.mts +90 -30
  8. package/dist/audit/index.d.ts +90 -30
  9. package/dist/identity/index.d.mts +1057 -280
  10. package/dist/identity/index.d.ts +1057 -280
  11. package/dist/identity/index.js +184 -29
  12. package/dist/identity/index.js.map +1 -1
  13. package/dist/identity/index.mjs +184 -29
  14. package/dist/identity/index.mjs.map +1 -1
  15. package/dist/index.d.mts +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +362 -335
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +362 -336
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/metadata-translations/index.js +20 -288
  22. package/dist/metadata-translations/index.js.map +1 -1
  23. package/dist/metadata-translations/index.mjs +20 -288
  24. package/dist/metadata-translations/index.mjs.map +1 -1
  25. package/dist/pages/index.d.mts +35 -4
  26. package/dist/pages/index.d.ts +35 -4
  27. package/dist/pages/index.js +112 -1
  28. package/dist/pages/index.js.map +1 -1
  29. package/dist/pages/index.mjs +112 -2
  30. package/dist/pages/index.mjs.map +1 -1
  31. package/dist/plugin.js +37 -300
  32. package/dist/plugin.js.map +1 -1
  33. package/dist/plugin.mjs +37 -300
  34. package/dist/plugin.mjs.map +1 -1
  35. package/dist/system/index.d.mts +30 -9
  36. package/dist/system/index.d.ts +30 -9
  37. package/dist/system/index.js +8 -0
  38. package/dist/system/index.js.map +1 -1
  39. package/dist/system/index.mjs +8 -0
  40. package/dist/system/index.mjs.map +1 -1
  41. package/package.json +3 -3
@@ -35,13 +35,14 @@ var SysUser = ObjectSchema.create({
35
35
  locations: ["list_toolbar"],
36
36
  type: "api",
37
37
  target: "/api/v1/auth/organization/invite-member",
38
- // Org invitations are a multi-org-only flow (the endpoint resolves
39
- // an active org that does not exist in single-org mode). Hide the
40
- // affordance unless multi-org is enabled matching the
41
- // `create_organization` gate on sys_organization. This action is the
42
- // most exposed of the set because the Users list is always reachable
43
- // in single-org, unlike the org/membership lists.
44
- visible: "features.multiOrgEnabled != false",
38
+ // Gated on the org CAPABILITY, not multi-org (ADR-0081 D1): the
39
+ // better-auth organization plugin is always mounted, and single-org
40
+ // mode now bootstraps a Default Organization (plugin-auth) so the
41
+ // endpoint's active-org resolution works there too. This is THE
42
+ // "add a teammate" affordance the Users list is always reachable
43
+ // and every add flows through better-auth invitations, never bespoke
44
+ // sys_user CRUD.
45
+ visible: "features.organization != false",
45
46
  successMessage: "Invitation sent",
46
47
  refreshAfter: true,
47
48
  params: [
@@ -100,6 +101,62 @@ var SysUser = ObjectSchema.create({
100
101
  successMessage: "Account unlocked",
101
102
  refreshAfter: true
102
103
  },
104
+ {
105
+ // #2766 V1 — a platform admin can add a login-capable teammate without
106
+ // the email-dependent invite flow. Hits the plugin-auth wrapper route
107
+ // (NOT better-auth's stock /admin/create-user): the wrapper runs the
108
+ // ADR-0068 admin gate, drives the better-auth pipeline (scrypt hash +
109
+ // credential sys_account), stamps must_change_password, and — when
110
+ // "Generate temporary password" is picked — returns the password ONCE
111
+ // in the response for the result dialog. It is never persisted or logged.
112
+ name: "create_user",
113
+ label: "Create User",
114
+ icon: "user-plus",
115
+ variant: "secondary",
116
+ locations: ["list_toolbar"],
117
+ type: "api",
118
+ target: "/api/v1/auth/admin/create-user",
119
+ visible: "features.admin == true",
120
+ successMessage: "User created",
121
+ refreshAfter: true,
122
+ params: [
123
+ // The endpoint requires email OR phone (phone-only users get a
124
+ // placeholder address; requires auth.plugins.phoneNumber).
125
+ { field: "email", required: false },
126
+ {
127
+ name: "phoneNumber",
128
+ label: "Phone Number",
129
+ type: "text",
130
+ required: false,
131
+ helpText: "Sign-in phone number (E.164, e.g. +8613800000000). Required when no email is given."
132
+ },
133
+ { field: "name", required: false },
134
+ {
135
+ name: "generatePassword",
136
+ label: "Generate Temporary Password",
137
+ type: "boolean",
138
+ required: false,
139
+ defaultValue: true
140
+ },
141
+ { name: "password", label: "Password (leave empty to generate)", type: "text", required: false },
142
+ {
143
+ name: "mustChangePassword",
144
+ label: "Require Password Change On First Login",
145
+ type: "boolean",
146
+ required: false,
147
+ defaultValue: true
148
+ }
149
+ ],
150
+ resultDialog: {
151
+ title: "User Created",
152
+ description: "Copy the temporary password now \u2014 it is shown only once and never stored.",
153
+ acknowledge: "I have saved this password",
154
+ fields: [
155
+ { path: "user.email", label: "Email", format: "text" },
156
+ { path: "temporaryPassword", label: "Temporary Password", format: "secret" }
157
+ ]
158
+ }
159
+ },
103
160
  {
104
161
  name: "set_user_password",
105
162
  label: "Set Password",
@@ -107,13 +164,40 @@ var SysUser = ObjectSchema.create({
107
164
  variant: "secondary",
108
165
  locations: ["list_item"],
109
166
  type: "api",
167
+ // #2766 V1 — same path as better-auth's stock route, but served by the
168
+ // plugin-auth wrapper registered ahead of the catch-all: it accepts the
169
+ // ADR-0068 platform-admin signals (the stock handler only honors the
170
+ // legacy role scalar), can mint a temporary password, and stamps
171
+ // must_change_password.
110
172
  target: "/api/v1/auth/admin/set-user-password",
111
173
  recordIdParam: "userId",
112
174
  successMessage: "Password updated",
113
175
  refreshAfter: false,
114
176
  params: [
115
- { name: "newPassword", label: "New Password", type: "text", required: true }
116
- ]
177
+ {
178
+ name: "generatePassword",
179
+ label: "Generate Temporary Password",
180
+ type: "boolean",
181
+ required: false,
182
+ defaultValue: false
183
+ },
184
+ { name: "newPassword", label: "New Password (leave empty to generate)", type: "text", required: false },
185
+ {
186
+ name: "mustChangePassword",
187
+ label: "Require Password Change On Next Login",
188
+ type: "boolean",
189
+ required: false,
190
+ defaultValue: true
191
+ }
192
+ ],
193
+ resultDialog: {
194
+ title: "Password Updated",
195
+ description: "If a temporary password was generated, copy it now \u2014 it is shown only once and never stored.",
196
+ acknowledge: "Done",
197
+ fields: [
198
+ { path: "temporaryPassword", label: "Temporary Password", format: "secret" }
199
+ ]
200
+ }
117
201
  },
118
202
  {
119
203
  name: "set_user_role",
@@ -432,6 +516,37 @@ var SysUser = ObjectSchema.create({
432
516
  group: "Admin",
433
517
  description: "Timestamp of the last password change. Backs password_expiry_days; system-managed."
434
518
  }),
519
+ // #2766 V1.5 — phone-number sign-in identifier (better-auth phoneNumber
520
+ // plugin, mapped via buildPhoneNumberPluginSchema). Unique when present;
521
+ // null for email-only accounts. Written through better-auth, not CRUD.
522
+ phone_number: Field.text({
523
+ label: "Phone Number",
524
+ required: false,
525
+ readonly: true,
526
+ searchable: true,
527
+ maxLength: 32,
528
+ group: "Account",
529
+ description: "Sign-in phone number (E.164 recommended). Unique per user; managed by better-auth when the phoneNumber plugin is enabled."
530
+ }),
531
+ phone_number_verified: Field.boolean({
532
+ label: "Phone Verified",
533
+ defaultValue: false,
534
+ readonly: true,
535
+ group: "Account",
536
+ description: "Whether the phone number has been verified (OTP verification requires SMS infrastructure; false until that ships). System-managed."
537
+ }),
538
+ // #2766 V1 — admin-issued "must change password on next sign-in" flag.
539
+ // Set by the /admin/create-user and /admin/set-user-password routes when
540
+ // a temporary password is issued; enforced through the ADR-0069 authGate
541
+ // (surfaces as PASSWORD_EXPIRED); cleared by stampPasswordChangedAt once
542
+ // the user completes a password change.
543
+ must_change_password: Field.boolean({
544
+ label: "Must Change Password",
545
+ defaultValue: false,
546
+ readonly: true,
547
+ group: "Admin",
548
+ description: "When true, the user is blocked (403 PASSWORD_EXPIRED) until they change their password. Stamped by the admin user-management routes; system-managed."
549
+ }),
435
550
  // ADR-0069 D3 — when enforced MFA first applied to this user; starts the
436
551
  // grace clock. Stamped lazily at session validation; system-managed.
437
552
  mfa_required_at: Field.datetime({
@@ -529,7 +644,10 @@ var SysUser = ObjectSchema.create({
529
644
  },
530
645
  indexes: [
531
646
  { fields: ["email"], unique: true },
532
- { fields: ["created_at"], unique: false }
647
+ { fields: ["created_at"], unique: false },
648
+ // #2766 V1.5 — phone sign-in identifier; unique when present (null for
649
+ // email-only accounts), also the upsert match key for identity import.
650
+ { fields: ["phone_number"], unique: true }
533
651
  ],
534
652
  enable: {
535
653
  trackHistory: true,
@@ -930,6 +1048,12 @@ var SysVerification = ObjectSchema.create({
930
1048
  icon: "shield-check",
931
1049
  isSystem: true,
932
1050
  managedBy: "better-auth",
1051
+ // [ADR-0066 D2/④] Secure-by-default: rows are LIVE one-time credentials
1052
+ // (email/phone verification + password-reset tokens) — reading one is
1053
+ // account takeover. Not covered by the wildcard `'*'` grant; admins retain
1054
+ // access via the superuser bypass; better-auth reads via its adapter
1055
+ // (system context), so verification flows are unaffected.
1056
+ access: { default: "private" },
933
1057
  // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema,
934
1058
  // but may add overlay row-level config. Use `no-overlay` if you need to
935
1059
  // forbid sys_metadata overlays entirely.
@@ -1263,11 +1387,11 @@ var SysMember = ObjectSchema.create({
1263
1387
  locations: ["list_toolbar"],
1264
1388
  type: "api",
1265
1389
  target: "/api/v1/auth/organization/add-member",
1266
- // Org-membership mutations are multi-org-only: the better-auth
1267
- // endpoints resolve an active org that does not exist in single-org
1268
- // mode, so these actions would fail at the API. Gate every one on
1269
- // the multi-org flag (mirrors sys_organization.create_organization).
1270
- visible: "features.multiOrgEnabled != false",
1390
+ // Gated on the org CAPABILITY, not multi-org (ADR-0081 D1): the
1391
+ // better-auth endpoints resolve the session's active org, which
1392
+ // single-org mode now guarantees via plugin-auth's default-org
1393
+ // bootstrap. Same gate on every membership mutation below.
1394
+ visible: "features.organization != false",
1271
1395
  successMessage: "Member added",
1272
1396
  refreshAfter: true,
1273
1397
  params: [
@@ -1285,7 +1409,7 @@ var SysMember = ObjectSchema.create({
1285
1409
  type: "api",
1286
1410
  target: "/api/v1/auth/organization/update-member-role",
1287
1411
  recordIdParam: "memberId",
1288
- visible: "features.multiOrgEnabled != false",
1412
+ visible: "features.organization != false",
1289
1413
  successMessage: "Member role updated",
1290
1414
  refreshAfter: true,
1291
1415
  params: [
@@ -1302,7 +1426,7 @@ var SysMember = ObjectSchema.create({
1302
1426
  type: "api",
1303
1427
  target: "/api/v1/auth/organization/remove-member",
1304
1428
  recordIdParam: "memberIdOrEmail",
1305
- visible: "features.multiOrgEnabled != false",
1429
+ visible: "features.organization != false",
1306
1430
  confirmText: "Remove this member from the organization? They will lose access to all org resources.",
1307
1431
  successMessage: "Member removed",
1308
1432
  refreshAfter: true
@@ -1324,7 +1448,7 @@ var SysMember = ObjectSchema.create({
1324
1448
  target: "/api/v1/auth/organization/update-member-role",
1325
1449
  recordIdParam: "memberId",
1326
1450
  bodyExtra: { role: "owner" },
1327
- visible: "record.role != 'owner' && features.multiOrgEnabled != false",
1451
+ visible: "record.role != 'owner' && features.organization != false",
1328
1452
  confirmText: "Transfer ownership of this organization to the selected member? You will be demoted to admin and lose owner-only privileges.",
1329
1453
  successMessage: "Ownership transferred",
1330
1454
  refreshAfter: true
@@ -1432,7 +1556,7 @@ var SysInvitation = ObjectSchema.create({
1432
1556
  // sys_organization.create_organization). The recipient-side
1433
1557
  // accept/reject actions below stay record-gated — they are
1434
1558
  // unreachable in single-org anyway (no invitation rows exist).
1435
- visible: "features.multiOrgEnabled != false",
1559
+ visible: "features.organization != false",
1436
1560
  successMessage: "Invitation sent",
1437
1561
  refreshAfter: true,
1438
1562
  params: [
@@ -1450,7 +1574,7 @@ var SysInvitation = ObjectSchema.create({
1450
1574
  type: "api",
1451
1575
  target: "/api/v1/auth/organization/cancel-invitation",
1452
1576
  recordIdParam: "invitationId",
1453
- visible: "features.multiOrgEnabled != false",
1577
+ visible: "features.organization != false",
1454
1578
  confirmText: "Cancel this invitation? The recipient will no longer be able to accept it.",
1455
1579
  successMessage: "Invitation canceled",
1456
1580
  refreshAfter: true
@@ -1464,7 +1588,7 @@ var SysInvitation = ObjectSchema.create({
1464
1588
  type: "api",
1465
1589
  target: "/api/v1/auth/organization/invite-member",
1466
1590
  bodyExtra: { resend: true },
1467
- visible: "features.multiOrgEnabled != false",
1591
+ visible: "features.organization != false",
1468
1592
  successMessage: "Invitation resent",
1469
1593
  refreshAfter: true,
1470
1594
  params: [
@@ -1654,7 +1778,7 @@ var SysTeam = ObjectSchema.create({
1654
1778
  // Teams are nested inside organizations — a multi-org-only concept.
1655
1779
  // Gate every team mutation on the multi-org flag so the affordances
1656
1780
  // disappear in single-org (mirrors sys_organization.create_organization).
1657
- visible: "features.multiOrgEnabled != false",
1781
+ visible: "features.organization != false",
1658
1782
  successMessage: "Team created",
1659
1783
  refreshAfter: true,
1660
1784
  params: [
@@ -1675,7 +1799,7 @@ var SysTeam = ObjectSchema.create({
1675
1799
  target: "/api/v1/auth/organization/update-team",
1676
1800
  recordIdParam: "teamId",
1677
1801
  bodyShape: { wrap: "data" },
1678
- visible: "features.multiOrgEnabled != false",
1802
+ visible: "features.organization != false",
1679
1803
  successMessage: "Team updated",
1680
1804
  refreshAfter: true,
1681
1805
  params: [
@@ -1694,7 +1818,7 @@ var SysTeam = ObjectSchema.create({
1694
1818
  type: "api",
1695
1819
  target: "/api/v1/auth/organization/remove-team",
1696
1820
  recordIdParam: "teamId",
1697
- visible: "features.multiOrgEnabled != false",
1821
+ visible: "features.organization != false",
1698
1822
  confirmText: "Delete this team? Members will lose any team-scoped access. This cannot be undone.",
1699
1823
  successMessage: "Team deleted",
1700
1824
  refreshAfter: true
@@ -1808,7 +1932,7 @@ var SysTeamMember = ObjectSchema.create({
1808
1932
  // Team membership lives under organizations — multi-org-only. Gate
1809
1933
  // both mutations so they vanish in single-org (mirrors
1810
1934
  // sys_organization.create_organization).
1811
- visible: "features.multiOrgEnabled != false",
1935
+ visible: "features.organization != false",
1812
1936
  successMessage: "Team member added",
1813
1937
  refreshAfter: true,
1814
1938
  params: [
@@ -1829,7 +1953,7 @@ var SysTeamMember = ObjectSchema.create({
1829
1953
  locations: ["list_item"],
1830
1954
  type: "api",
1831
1955
  target: "/api/v1/auth/organization/remove-team-member",
1832
- visible: "features.multiOrgEnabled != false",
1956
+ visible: "features.organization != false",
1833
1957
  confirmText: "Remove this user from the team? They will lose any team-scoped access.",
1834
1958
  successMessage: "Team member removed",
1835
1959
  refreshAfter: true,
@@ -2067,7 +2191,7 @@ var SysBusinessUnitMember = ObjectSchema.create({
2067
2191
  managedBy: "platform",
2068
2192
  description: "User assignment to a business unit (matrix-org friendly, effective-dated).",
2069
2193
  titleFormat: "{user_id} in {business_unit_id}",
2070
- highlightFields: ["user_id", "business_unit_id", "role_in_business_unit", "is_primary"],
2194
+ highlightFields: ["user_id", "business_unit_id", "function_in_business_unit", "is_primary"],
2071
2195
  fields: {
2072
2196
  id: Field.text({
2073
2197
  label: "Member ID",
@@ -2085,10 +2209,10 @@ var SysBusinessUnitMember = ObjectSchema.create({
2085
2209
  required: true,
2086
2210
  group: "Assignment"
2087
2211
  }),
2088
- role_in_business_unit: Field.select(
2212
+ function_in_business_unit: Field.select(
2089
2213
  ["member", "lead", "deputy"],
2090
2214
  {
2091
- label: "Role in Business Unit",
2215
+ label: "Function in Business Unit",
2092
2216
  required: false,
2093
2217
  defaultValue: "member",
2094
2218
  description: "`lead` is the day-to-day head; `deputy` may stand in for the lead in approval routing.",
@@ -2492,6 +2616,12 @@ var SysDeviceCode = ObjectSchema.create({
2492
2616
  icon: "key-round",
2493
2617
  isSystem: true,
2494
2618
  managedBy: "better-auth",
2619
+ // [ADR-0066 D2/④] Secure-by-default: rows are LIVE pending device-grant
2620
+ // codes — reading `user_code`/`device_code` lets an attacker hijack a
2621
+ // pending CLI login. Not covered by the wildcard `'*'` grant; admins retain
2622
+ // access via the superuser bypass; better-auth reads via its adapter
2623
+ // (system context), so the device-grant flow is unaffected.
2624
+ access: { default: "private" },
2495
2625
  // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema,
2496
2626
  // but may add overlay row-level config. Use `no-overlay` if you need to
2497
2627
  // forbid sys_metadata overlays entirely.
@@ -3087,6 +3217,11 @@ var SysOauthAccessToken = ObjectSchema.create({
3087
3217
  icon: "ticket",
3088
3218
  isSystem: true,
3089
3219
  managedBy: "better-auth",
3220
+ // [ADR-0066 D2/④] Secure-by-default: rows are LIVE bearer credentials —
3221
+ // reading one is session hijack. Not covered by the wildcard `'*'` grant;
3222
+ // admins retain access via the superuser bypass; better-auth reads via its
3223
+ // adapter (system context), so OAuth flows are unaffected.
3224
+ access: { default: "private" },
3090
3225
  // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema,
3091
3226
  // but may add overlay row-level config. Use `no-overlay` if you need to
3092
3227
  // forbid sys_metadata overlays entirely.
@@ -3173,6 +3308,11 @@ var SysOauthRefreshToken = ObjectSchema.create({
3173
3308
  icon: "refresh-cw",
3174
3309
  isSystem: true,
3175
3310
  managedBy: "better-auth",
3311
+ // [ADR-0066 D2/④] Secure-by-default: rows are LIVE long-lived credentials —
3312
+ // a refresh token mints new access tokens. Not covered by the wildcard `'*'`
3313
+ // grant; admins retain access via the superuser bypass; better-auth reads
3314
+ // via its adapter (system context), so OAuth flows are unaffected.
3315
+ access: { default: "private" },
3176
3316
  // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema,
3177
3317
  // but may add overlay row-level config. Use `no-overlay` if you need to
3178
3318
  // forbid sys_metadata overlays entirely.
@@ -3331,6 +3471,13 @@ var SysJwks = ObjectSchema.create({
3331
3471
  icon: "key",
3332
3472
  isSystem: true,
3333
3473
  managedBy: "better-auth",
3474
+ // [ADR-0066 D2/④] Secure-by-default: rows are the environment's JWT SIGNING
3475
+ // KEYS (private key material). Not covered by the wildcard `'*'` grant — an
3476
+ // ordinary member gets 403 from the generic data layer. Platform admins
3477
+ // (viewAllRecords/modifyAllRecords) retain access via the posture-gated
3478
+ // superuser bypass; better-auth itself reads via its adapter (system
3479
+ // context), so token signing/verification is unaffected.
3480
+ access: { default: "private" },
3334
3481
  // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema,
3335
3482
  // but may add overlay row-level config. Use `no-overlay` if you need to
3336
3483
  // forbid sys_metadata overlays entirely.
@@ -3644,6 +3791,14 @@ var SysScimProvider = ObjectSchema.create({
3644
3791
  icon: "users",
3645
3792
  isSystem: true,
3646
3793
  managedBy: "better-auth",
3794
+ // [ADR-0066 D3/④] Admin-only identity config carrying a live credential
3795
+ // (`scim_token` — the bearer external IdPs authenticate provisioning calls
3796
+ // with). Object-level capability gate, mirroring the sibling
3797
+ // `sys_sso_provider`: ordinary members are denied entirely (without it, the
3798
+ // `member_default` wildcard `'*': allowRead` would expose SCIM connections
3799
+ // to every authenticated user). better-auth's own endpoints read via a
3800
+ // system context, so SCIM provisioning is unaffected.
3801
+ requiredPermissions: ["manage_platform_settings"],
3647
3802
  // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema.
3648
3803
  protection: {
3649
3804
  lock: "full",