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