@owlmeans/viable-common 0.0.46 → 0.0.47

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 (153) hide show
  1. package/README.md +163 -14
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/agent-presentation/SKILL.md +1 -1
  4. package/agent-meta/skills/viable-common/SKILL.md +122 -30
  5. package/build/connect/account/types.d.ts +81 -0
  6. package/build/connect/account/types.d.ts.map +1 -0
  7. package/build/connect/account/types.js +2 -0
  8. package/build/connect/account/types.js.map +1 -0
  9. package/build/connect/branding/types.d.ts +26 -4
  10. package/build/connect/branding/types.d.ts.map +1 -1
  11. package/build/connect/call/types.d.ts +28 -0
  12. package/build/connect/call/types.d.ts.map +1 -0
  13. package/build/connect/call/types.js +2 -0
  14. package/build/connect/call/types.js.map +1 -0
  15. package/build/connect/config/types.d.ts +45 -0
  16. package/build/connect/config/types.d.ts.map +1 -0
  17. package/build/connect/config/types.js +2 -0
  18. package/build/connect/config/types.js.map +1 -0
  19. package/build/connect/consts.d.ts +383 -1
  20. package/build/connect/consts.d.ts.map +1 -1
  21. package/build/connect/consts.js +387 -1
  22. package/build/connect/consts.js.map +1 -1
  23. package/build/connect/conversion/types.d.ts +24 -0
  24. package/build/connect/conversion/types.d.ts.map +1 -1
  25. package/build/connect/entrypoints/types.d.ts +22 -13
  26. package/build/connect/entrypoints/types.d.ts.map +1 -1
  27. package/build/connect/entrypoints.d.ts +441 -8
  28. package/build/connect/entrypoints.d.ts.map +1 -1
  29. package/build/connect/entrypoints.js +292 -9
  30. package/build/connect/entrypoints.js.map +1 -1
  31. package/build/connect/errors.d.ts +12 -0
  32. package/build/connect/errors.d.ts.map +1 -1
  33. package/build/connect/errors.js +16 -0
  34. package/build/connect/errors.js.map +1 -1
  35. package/build/connect/feed/types.d.ts +71 -0
  36. package/build/connect/feed/types.d.ts.map +1 -0
  37. package/build/connect/feed/types.js +2 -0
  38. package/build/connect/feed/types.js.map +1 -0
  39. package/build/connect/files/types.d.ts +34 -0
  40. package/build/connect/files/types.d.ts.map +1 -0
  41. package/build/connect/files/types.js +2 -0
  42. package/build/connect/files/types.js.map +1 -0
  43. package/build/connect/git/types.d.ts +170 -0
  44. package/build/connect/git/types.d.ts.map +1 -0
  45. package/build/connect/git/types.js +14 -0
  46. package/build/connect/git/types.js.map +1 -0
  47. package/build/connect/iam/types.d.ts +25 -0
  48. package/build/connect/iam/types.d.ts.map +1 -0
  49. package/build/connect/iam/types.js +8 -0
  50. package/build/connect/iam/types.js.map +1 -0
  51. package/build/connect/index.d.ts +9 -0
  52. package/build/connect/index.d.ts.map +1 -1
  53. package/build/connect/production/types.d.ts +65 -0
  54. package/build/connect/production/types.d.ts.map +1 -0
  55. package/build/connect/production/types.js +2 -0
  56. package/build/connect/production/types.js.map +1 -0
  57. package/build/connect/project/types.d.ts +23 -16
  58. package/build/connect/project/types.d.ts.map +1 -1
  59. package/build/connect/references/types.d.ts +424 -2
  60. package/build/connect/references/types.d.ts.map +1 -1
  61. package/build/connect/references.d.ts.map +1 -1
  62. package/build/connect/references.js +122 -0
  63. package/build/connect/references.js.map +1 -1
  64. package/build/connect/schemas.d.ts +65 -2
  65. package/build/connect/schemas.d.ts.map +1 -1
  66. package/build/connect/schemas.js +306 -1
  67. package/build/connect/schemas.js.map +1 -1
  68. package/build/connect/settings/types.d.ts +0 -25
  69. package/build/connect/settings/types.d.ts.map +1 -1
  70. package/build/connect/slot/types.d.ts +24 -0
  71. package/build/connect/slot/types.d.ts.map +1 -0
  72. package/build/connect/slot/types.js +2 -0
  73. package/build/connect/slot/types.js.map +1 -0
  74. package/build/convert/record/types.d.ts +0 -9
  75. package/build/convert/record/types.d.ts.map +1 -1
  76. package/build/convert/schemas.d.ts +2 -5
  77. package/build/convert/schemas.d.ts.map +1 -1
  78. package/build/convert/schemas.js +2 -15
  79. package/build/convert/schemas.js.map +1 -1
  80. package/build/iam-console/consts.d.ts +13 -0
  81. package/build/iam-console/consts.d.ts.map +1 -0
  82. package/build/iam-console/consts.js +13 -0
  83. package/build/iam-console/consts.js.map +1 -0
  84. package/build/iam-console/consts.local.d.ts +123 -0
  85. package/build/iam-console/consts.local.d.ts.map +1 -0
  86. package/build/iam-console/consts.local.js +50 -0
  87. package/build/iam-console/consts.local.js.map +1 -0
  88. package/build/iam-console/index.d.ts +4 -0
  89. package/build/iam-console/index.d.ts.map +1 -0
  90. package/build/iam-console/index.js +3 -0
  91. package/build/iam-console/index.js.map +1 -0
  92. package/build/iam-console/schemas.d.ts +38 -0
  93. package/build/iam-console/schemas.d.ts.map +1 -0
  94. package/build/iam-console/schemas.js +217 -0
  95. package/build/iam-console/schemas.js.map +1 -0
  96. package/build/iam-console/types.d.ts +140 -0
  97. package/build/iam-console/types.d.ts.map +1 -0
  98. package/build/iam-console/types.js +2 -0
  99. package/build/iam-console/types.js.map +1 -0
  100. package/build/index.d.ts +1 -0
  101. package/build/index.d.ts.map +1 -1
  102. package/build/index.js +1 -0
  103. package/build/index.js.map +1 -1
  104. package/build/legal/consts.d.ts +33 -2
  105. package/build/legal/consts.d.ts.map +1 -1
  106. package/build/legal/consts.js +11 -3
  107. package/build/legal/consts.js.map +1 -1
  108. package/build/legal/types.d.ts +8 -1
  109. package/build/legal/types.d.ts.map +1 -1
  110. package/build/planning/schemas.d.ts.map +1 -1
  111. package/build/planning/schemas.js +0 -1
  112. package/build/planning/schemas.js.map +1 -1
  113. package/build/planning/types.d.ts +0 -2
  114. package/build/planning/types.d.ts.map +1 -1
  115. package/package.json +11 -2
  116. package/src/connect/account/types.ts +91 -0
  117. package/src/connect/branding/types.ts +28 -4
  118. package/src/connect/call/types.ts +31 -0
  119. package/src/connect/config/types.ts +51 -0
  120. package/src/connect/consts.ts +400 -1
  121. package/src/connect/conversion/types.ts +25 -0
  122. package/src/connect/entrypoints/types.ts +22 -7
  123. package/src/connect/entrypoints.ts +592 -15
  124. package/src/connect/errors.ts +18 -0
  125. package/src/connect/feed/types.ts +76 -0
  126. package/src/connect/files/types.ts +38 -0
  127. package/src/connect/git/types.ts +187 -0
  128. package/src/connect/iam/types.ts +28 -0
  129. package/src/connect/index.ts +9 -0
  130. package/src/connect/production/types.ts +71 -0
  131. package/src/connect/project/types.ts +25 -12
  132. package/src/connect/references/types.ts +166 -5
  133. package/src/connect/references.ts +210 -5
  134. package/src/connect/schemas.ts +366 -5
  135. package/src/connect/settings/types.ts +0 -25
  136. package/src/connect/slot/types.ts +24 -0
  137. package/src/convert/record/types.ts +0 -11
  138. package/src/convert/schemas.ts +3 -19
  139. package/src/iam-console/consts.local.ts +64 -0
  140. package/src/iam-console/consts.ts +15 -0
  141. package/src/iam-console/index.ts +3 -0
  142. package/src/iam-console/schemas.ts +251 -0
  143. package/src/iam-console/types.ts +165 -0
  144. package/src/index.ts +1 -0
  145. package/src/legal/consts.ts +13 -5
  146. package/src/legal/types.ts +12 -1
  147. package/src/planning/schemas.ts +0 -1
  148. package/src/planning/types.ts +0 -2
  149. package/tests/connect-convert.spec.ts +6 -2
  150. package/tests/connect-entrypoints.spec.ts +554 -16
  151. package/tests/error-status.spec.ts +2 -1
  152. package/tests/iam-console.spec.ts +89 -0
  153. package/tests/planning.spec.ts +8 -0
@@ -0,0 +1,251 @@
1
+ import type { JSONSchemaType } from 'ajv'
2
+ import { IamDefaultClass, type IamGroupRef } from '@owlmeans/iam'
3
+ import {
4
+ EmailSchema, EntitySlugSchema, GroupBundleSchema, GroupKeySchema, ModeProp, NameSchema,
5
+ PermissionNameSchema, ProfileIdSchema, ProfileIdsSchema, ProjectIdProp, ResourceIdSchema, RoleSchema, ScopeProp,
6
+ TitleSchema,
7
+ } from './consts.local.js'
8
+ import type {
9
+ IamAssignGrant, IamDefinitionUpdate, IamGrantsQuery, IamGroupCreate, IamGroupEdit, IamGroupMembersChange,
10
+ IamGroupParams, IamMemberParams, IamOrganizationEdit, IamOrganizationParams, IamProjectMemberInvite,
11
+ IamProjectMemberUpdate, IamProjectParams, IamProjectUserInvite, IamProjectUserParams, IamProjectUserUpdate,
12
+ IamRevokeGrant, IamScoped, IamStaffSync,
13
+ } from './types.js'
14
+
15
+ /**
16
+ * The owner console's IAM schemas — one set for both doors: the browser's `back.iam` routes and the
17
+ * connector's `connect.iam` twins validate the same bodies, so a field cannot be admitted on one and
18
+ * refused on the other. Every object is closed (`additionalProperties: false`): an organization's
19
+ * record id (`entityId`) is refused wherever it is offered.
20
+ */
21
+
22
+ export const IamProjectParamsSchema: JSONSchemaType<IamProjectParams> = {
23
+ type: 'object',
24
+ properties: { ...ProjectIdProp },
25
+ required: ['projectId'],
26
+ additionalProperties: false,
27
+ }
28
+
29
+ export const IamProjectUserParamsSchema: JSONSchemaType<IamProjectUserParams> = {
30
+ type: 'object',
31
+ properties: { ...ProjectIdProp, profileId: ProfileIdSchema },
32
+ required: ['projectId', 'profileId'],
33
+ additionalProperties: false,
34
+ } as unknown as JSONSchemaType<IamProjectUserParams>
35
+
36
+ export const IamOrganizationParamsSchema: JSONSchemaType<IamOrganizationParams> = {
37
+ type: 'object',
38
+ properties: { ...ProjectIdProp, entitySlug: EntitySlugSchema },
39
+ required: ['projectId', 'entitySlug'],
40
+ additionalProperties: false,
41
+ } as unknown as JSONSchemaType<IamOrganizationParams>
42
+
43
+ export const IamMemberParamsSchema: JSONSchemaType<IamMemberParams> = {
44
+ type: 'object',
45
+ properties: { ...ProjectIdProp, entitySlug: EntitySlugSchema, profileId: ProfileIdSchema },
46
+ required: ['projectId', 'entitySlug', 'profileId'],
47
+ additionalProperties: false,
48
+ } as unknown as JSONSchemaType<IamMemberParams>
49
+
50
+ export const IamGroupRefSchema: JSONSchemaType<IamGroupRef> = {
51
+ type: 'object',
52
+ properties: {
53
+ entitySlug: EntitySlugSchema,
54
+ key: GroupKeySchema,
55
+ },
56
+ required: ['entitySlug', 'key'],
57
+ additionalProperties: false,
58
+ } as unknown as JSONSchemaType<IamGroupRef>
59
+
60
+ export const IamGroupParamsSchema: JSONSchemaType<IamGroupParams> = {
61
+ type: 'object',
62
+ properties: { ...ProjectIdProp, entitySlug: EntitySlugSchema, group: GroupKeySchema },
63
+ required: ['projectId', 'entitySlug', 'group'],
64
+ additionalProperties: false,
65
+ } as unknown as JSONSchemaType<IamGroupParams>
66
+
67
+ /** The client a body-less write addresses, carried in the body where a route has one (`scope`). */
68
+ export const IamScopedSchema: JSONSchemaType<IamScoped> = {
69
+ type: 'object',
70
+ properties: { scope: ScopeProp },
71
+ required: [],
72
+ additionalProperties: false,
73
+ } as unknown as JSONSchemaType<IamScoped>
74
+
75
+ /**
76
+ * A person or a group, never both; a group always with the organization it lives in.
77
+ *
78
+ * `dependencies` rather than a handler check, so a malformed listing is a validation refusal like
79
+ * every other — and the handler can rely on the shape it was handed.
80
+ */
81
+ export const IamGrantsQuerySchema: JSONSchemaType<IamGrantsQuery> = {
82
+ type: 'object',
83
+ properties: {
84
+ profileId: { ...ProfileIdSchema, nullable: true },
85
+ group: { ...GroupKeySchema, nullable: true },
86
+ entitySlug: { ...EntitySlugSchema, nullable: true },
87
+ scope: ScopeProp,
88
+ },
89
+ required: [],
90
+ additionalProperties: false,
91
+ dependencies: { group: ['entitySlug'] },
92
+ not: { required: ['profileId', 'group'] },
93
+ } as unknown as JSONSchemaType<IamGrantsQuery>
94
+
95
+ /**
96
+ * Exactly one subject. Each branch excludes the other subject itself, so a body naming both
97
+ * matches neither; the group branch also refuses a binding — a group grant is bound to the group's
98
+ * own organization, and a second organization in the body could only disagree with it. Each branch
99
+ * declares the property it requires, so a reader of the branch alone (a model-schema check) sees it.
100
+ */
101
+ const OneSubject = [
102
+ { properties: { profileId: ProfileIdSchema }, required: ['profileId'], not: { required: ['group'] } },
103
+ {
104
+ properties: { group: IamGroupRefSchema },
105
+ required: ['group'],
106
+ not: { anyOf: [{ required: ['profileId'] }, { required: ['entitySlug'] }] },
107
+ },
108
+ ] as const
109
+
110
+ const grantSchemaProperties = {
111
+ profileId: { ...ProfileIdSchema, nullable: true },
112
+ group: { ...IamGroupRefSchema, nullable: true },
113
+ permission: PermissionNameSchema,
114
+ resources: { type: 'array', items: ResourceIdSchema, maxItems: 256, nullable: true },
115
+ mode: ModeProp,
116
+ entitySlug: { ...EntitySlugSchema, nullable: true },
117
+ scope: ScopeProp,
118
+ } as const
119
+
120
+ export const IamAssignGrantSchema: JSONSchemaType<IamAssignGrant> = {
121
+ type: 'object',
122
+ properties: grantSchemaProperties,
123
+ required: ['permission'],
124
+ additionalProperties: false,
125
+ oneOf: OneSubject,
126
+ } as unknown as JSONSchemaType<IamAssignGrant>
127
+
128
+ export const IamRevokeGrantSchema: JSONSchemaType<IamRevokeGrant> = {
129
+ type: 'object',
130
+ properties: grantSchemaProperties,
131
+ required: ['permission'],
132
+ additionalProperties: false,
133
+ oneOf: OneSubject,
134
+ } as unknown as JSONSchemaType<IamRevokeGrant>
135
+
136
+ const DefaultClassProp = { type: 'string', enum: [...Object.values(IamDefaultClass), null], nullable: true } as const
137
+ const EntityScopedProp = { type: 'boolean', nullable: true } as const
138
+
139
+ /** At least one setting: an update that changes nothing is a client bug, not a request. */
140
+ export const IamDefinitionUpdateSchema: JSONSchemaType<IamDefinitionUpdate> = {
141
+ type: 'object',
142
+ properties: {
143
+ permission: PermissionNameSchema,
144
+ defaultClass: DefaultClassProp,
145
+ entityScoped: EntityScopedProp,
146
+ scope: ScopeProp,
147
+ },
148
+ required: ['permission'],
149
+ additionalProperties: false,
150
+ // Each branch declares the property it requires, so a reader of the branch alone sees it.
151
+ anyOf: [
152
+ { properties: { defaultClass: DefaultClassProp }, required: ['defaultClass'] },
153
+ { properties: { entityScoped: EntityScopedProp }, required: ['entityScoped'] },
154
+ ],
155
+ } as unknown as JSONSchemaType<IamDefinitionUpdate>
156
+
157
+ export const IamStaffSyncSchema: JSONSchemaType<IamStaffSync> = {
158
+ type: 'object',
159
+ properties: { scope: ScopeProp },
160
+ required: [],
161
+ additionalProperties: false,
162
+ } as unknown as JSONSchemaType<IamStaffSync>
163
+
164
+ export const IamProjectUserInviteSchema: JSONSchemaType<IamProjectUserInvite> = {
165
+ type: 'object',
166
+ properties: {
167
+ email: EmailSchema,
168
+ name: { ...NameSchema, nullable: true },
169
+ role: { ...RoleSchema, nullable: true },
170
+ scope: ScopeProp,
171
+ },
172
+ required: ['email'],
173
+ additionalProperties: false,
174
+ } as unknown as JSONSchemaType<IamProjectUserInvite>
175
+
176
+ export const IamProjectUserUpdateSchema: JSONSchemaType<IamProjectUserUpdate> = {
177
+ type: 'object',
178
+ properties: {
179
+ name: { ...NameSchema, nullable: true },
180
+ role: { ...RoleSchema, nullable: true },
181
+ disabled: { type: 'boolean', nullable: true },
182
+ scope: ScopeProp,
183
+ },
184
+ required: [],
185
+ additionalProperties: false,
186
+ } as unknown as JSONSchemaType<IamProjectUserUpdate>
187
+
188
+ export const IamOrganizationEditSchema: JSONSchemaType<IamOrganizationEdit> = {
189
+ type: 'object',
190
+ properties: {
191
+ title: TitleSchema,
192
+ scope: ScopeProp,
193
+ },
194
+ required: ['title'],
195
+ additionalProperties: false,
196
+ } as unknown as JSONSchemaType<IamOrganizationEdit>
197
+
198
+ export const IamProjectMemberInviteSchema: JSONSchemaType<IamProjectMemberInvite> = {
199
+ type: 'object',
200
+ properties: {
201
+ email: EmailSchema,
202
+ name: { ...NameSchema, nullable: true },
203
+ owner: { type: 'boolean', nullable: true },
204
+ scope: ScopeProp,
205
+ },
206
+ required: ['email'],
207
+ additionalProperties: false,
208
+ } as unknown as JSONSchemaType<IamProjectMemberInvite>
209
+
210
+ export const IamProjectMemberUpdateSchema: JSONSchemaType<IamProjectMemberUpdate> = {
211
+ type: 'object',
212
+ properties: {
213
+ owner: { type: 'boolean', nullable: true },
214
+ groups: { type: 'array', items: GroupKeySchema, maxItems: 64, nullable: true },
215
+ scope: ScopeProp,
216
+ },
217
+ required: [],
218
+ additionalProperties: false,
219
+ } as unknown as JSONSchemaType<IamProjectMemberUpdate>
220
+
221
+ export const IamGroupCreateSchema: JSONSchemaType<IamGroupCreate> = {
222
+ type: 'object',
223
+ properties: {
224
+ key: GroupKeySchema,
225
+ title: { ...TitleSchema, nullable: true },
226
+ scope: ScopeProp,
227
+ },
228
+ required: ['key'],
229
+ additionalProperties: false,
230
+ } as unknown as JSONSchemaType<IamGroupCreate>
231
+
232
+ export const IamGroupEditSchema: JSONSchemaType<IamGroupEdit> = {
233
+ type: 'object',
234
+ properties: {
235
+ title: { ...TitleSchema, nullable: true },
236
+ bundles: { type: 'array', items: GroupBundleSchema, maxItems: 32, nullable: true },
237
+ scope: ScopeProp,
238
+ },
239
+ required: [],
240
+ additionalProperties: false,
241
+ } as unknown as JSONSchemaType<IamGroupEdit>
242
+
243
+ export const IamGroupMembersChangeSchema: JSONSchemaType<IamGroupMembersChange> = {
244
+ type: 'object',
245
+ properties: {
246
+ profileIds: ProfileIdsSchema,
247
+ scope: ScopeProp,
248
+ },
249
+ required: ['profileIds'],
250
+ additionalProperties: false,
251
+ } as unknown as JSONSchemaType<IamGroupMembersChange>
@@ -0,0 +1,165 @@
1
+ import type {
2
+ IamDefaultClass, IamGrant, IamGrantMode, IamGroup, IamGroupBundle, IamGroupRef, IamMember, IamMemberInvite,
3
+ IamMemberUpdate, IamOrganization, IamOrganizationUpdate, IamPermissionDefinition, IamUser, IamUserInvite,
4
+ IamUserUpdate,
5
+ } from '@owlmeans/iam'
6
+ import type { WorkloadKind } from '../slot/consts.js'
7
+
8
+ /**
9
+ * The owner console's IAM, per project — the contract both doors share: the browser's
10
+ * `managerProtocols.back.iam` and the connector's `connect.iam`. Every route names its project, and
11
+ * `scope` picks the preview (ephemeral, default) or the production OIDC client — two apps with two
12
+ * sets of rows, definitions and groups. Organizations travel by `entitySlug` and groups by their key:
13
+ * an organization's record id never appears on this wire, and neither does the owner's.
14
+ */
15
+
16
+ /** The IAM vocabulary a consumer of this contract reads and writes, re-exported. */
17
+ export type {
18
+ IamArea, IamGrant, IamGrantBundle, IamGroup, IamGroupBundle, IamGroupRef, IamMember, IamMemberInvite,
19
+ IamMemberUpdate, IamOrganization, IamOrganizationUpdate, IamPermissionDefinition, IamPermissionFilter, IamUser,
20
+ IamUserInvite, IamUserUpdate,
21
+ } from '@owlmeans/iam'
22
+
23
+ /** Route params for the project-scoped IAM management endpoints. */
24
+ export interface IamProjectParams {
25
+ projectId: string
26
+ }
27
+
28
+ /** One subject of the project's client, by its pairwise `profileId`. */
29
+ export interface IamProjectUserParams extends IamProjectParams {
30
+ profileId: string
31
+ }
32
+
33
+ /** One organization the project's client has members in. */
34
+ export interface IamOrganizationParams extends IamProjectParams {
35
+ entitySlug: string
36
+ }
37
+
38
+ /** One member of an organization, as the project's client sees them. */
39
+ export interface IamMemberParams extends IamOrganizationParams {
40
+ profileId: string
41
+ }
42
+
43
+ /** One group of (organization, the project's client), by its key. */
44
+ export interface IamGroupParams extends IamOrganizationParams {
45
+ group: string
46
+ }
47
+
48
+ /**
49
+ * Which of the project's two clients a write addresses. A route with a body carries it there; a
50
+ * body-less write (the browser's removes) and every read take it in the query.
51
+ */
52
+ export interface IamScoped {
53
+ scope?: WorkloadKind
54
+ }
55
+
56
+ /**
57
+ * A listing. `external` answers a backend that keeps this in a console of its own (keycloak): the
58
+ * list is then empty on purpose, and a screen offers that console instead of reporting "none".
59
+ */
60
+ export interface IamListResponse<T> {
61
+ items: T[]
62
+ external?: boolean
63
+ }
64
+
65
+ export interface IamUsersResponse extends IamListResponse<IamUser> {}
66
+
67
+ export interface IamGrantsResponse extends IamListResponse<IamGrant> {}
68
+
69
+ export interface IamOrganizationsResponse extends IamListResponse<IamOrganization> {}
70
+
71
+ export interface IamMembersResponse extends IamListResponse<IamMember> {}
72
+
73
+ export interface IamGroupsResponse extends IamListResponse<IamGroup> {}
74
+
75
+ /** Which organizations the client's subjects act in — its registration's two tenancy flags. */
76
+ export interface IamTenancy {
77
+ operators: boolean
78
+ users: boolean
79
+ }
80
+
81
+ export interface IamPermissionsResponse extends IamListResponse<IamPermissionDefinition> {
82
+ tenancy: IamTenancy
83
+ }
84
+
85
+ /**
86
+ * Query for listing grants: everything the client's subjects hold, one person's (`profileId`), or
87
+ * one group's (`group`, the key, with the `entitySlug` of its organization). With a person, an
88
+ * `entitySlug` alone narrows to the grants bound to that organization.
89
+ */
90
+ export interface IamGrantsQuery extends IamScoped {
91
+ profileId?: string
92
+ group?: string
93
+ entitySlug?: string
94
+ }
95
+
96
+ /**
97
+ * Change a definition's settings: who holds it by default (`defaultClass`) and whether its grants
98
+ * are bound to one organization (`entityScoped`). An omitted field is kept.
99
+ */
100
+ export interface IamDefinitionUpdate extends IamScoped {
101
+ permission: string
102
+ defaultClass?: IamDefaultClass
103
+ entityScoped?: boolean
104
+ }
105
+
106
+ /** Re-admit the owning organization's staff to the client `scope` names. */
107
+ export interface IamStaffSync extends IamScoped {}
108
+
109
+ /** How many staff rows the synchronization added and removed. */
110
+ export interface IamStaffSyncResponse {
111
+ added: number
112
+ removed: number
113
+ }
114
+
115
+ /**
116
+ * Body to assign a permission grant — to exactly ONE subject: a person (`profileId`) or a group.
117
+ *
118
+ * A resource-scoped permission is grantable two ways under ONE name: with `resources`, bound to
119
+ * those ids; without them, covering every resource. `mode` names which, because the two are stored
120
+ * separately and a screen offering both controls has to be able to address each on its own.
121
+ *
122
+ * `entitySlug` binds a person's grant of an entity-scoped definition to that organization. A group
123
+ * grant is always bound to the group's own organization, so it takes none.
124
+ */
125
+ export interface IamAssignGrant extends IamScoped {
126
+ profileId?: string
127
+ group?: IamGroupRef
128
+ permission: string
129
+ resources?: string[]
130
+ /** `blanket` (all resources) or `resources`. Inferred from `resources` when omitted. */
131
+ mode?: IamGrantMode
132
+ entitySlug?: string
133
+ }
134
+
135
+ /**
136
+ * Body to revoke a grant, the same shape. With `resources` only those ids are removed; an omitted
137
+ * `mode` means ALL forms, which is what a bare revoke has always meant — narrowing that default
138
+ * would leave a resource-scoped grant standing after a revoke reported success.
139
+ */
140
+ export interface IamRevokeGrant extends IamAssignGrant {}
141
+
142
+ export interface IamProjectUserInvite extends IamUserInvite, IamScoped {}
143
+
144
+ export interface IamProjectUserUpdate extends IamUserUpdate, IamScoped {}
145
+
146
+ export interface IamOrganizationEdit extends IamOrganizationUpdate, IamScoped {}
147
+
148
+ export interface IamProjectMemberInvite extends IamMemberInvite, IamScoped {}
149
+
150
+ export interface IamProjectMemberUpdate extends IamMemberUpdate, IamScoped {}
151
+
152
+ export interface IamGroupCreate extends IamScoped {
153
+ key: string
154
+ title?: string
155
+ }
156
+
157
+ /** A group's title and what it grants; an omitted field is kept. */
158
+ export interface IamGroupEdit extends IamScoped {
159
+ title?: string
160
+ bundles?: IamGroupBundle[]
161
+ }
162
+
163
+ export interface IamGroupMembersChange extends IamScoped {
164
+ profileIds: string[]
165
+ }
package/src/index.ts CHANGED
@@ -22,3 +22,4 @@ export * from './design/index.js'
22
22
  export * from './planning/index.js'
23
23
  export * from './branding.js'
24
24
  export * from './legal/index.js'
25
+ export * from './iam-console/index.js'
@@ -1,9 +1,10 @@
1
- import type { LegalDocumentDates, LegalDocumentKey } from './types.js'
1
+ import type { LegalDocumentDateMetadata, LegalDocumentKey } from './types.js'
2
2
 
3
3
  /**
4
4
  * The shared dates for the platform's legal acceptance and the public site's legal pages.
5
- * Changing terms/privacy/billing/product updated dates requires renewed terms acceptance;
6
- * privacy.updated also revises marketing consent. The other documents are display-only.
5
+ * Consumers select the actual agreement keys for acceptance. Optional purposes carry their own
6
+ * wording revisions, independent of this table. Pending documents have no effective/update date;
7
+ * a coordinated publication and acceptance release supplies those dates when activated.
7
8
  */
8
9
  export const OWLMEANS_LEGAL_DATES = {
9
10
  terms: { effective: '2026-05-30', updated: '2026-05-30' },
@@ -11,7 +12,14 @@ export const OWLMEANS_LEGAL_DATES = {
11
12
  cookies: { effective: '2026-09-24', updated: '2026-09-25' },
12
13
  'services-agreement': { effective: '2026-10-04', updated: '2026-10-04' },
13
14
  'platform-license': { effective: '2026-10-04', updated: '2026-10-04' },
14
- // These documents date the platform's sign-in terms line without public-site pages yet.
15
+ 'data-processing-agreement': { status: 'pending', effective: null, updated: null },
16
+ subprocessors: { status: 'pending', effective: null, updated: null },
17
+ 'company-information': { status: 'pending', effective: null, updated: null },
18
+ 'acceptable-use': { status: 'pending', effective: null, updated: null },
19
+ 'refund-cancellation': { status: 'pending', effective: null, updated: null },
20
+ 'service-level-agreement': { status: 'pending', effective: null, updated: null },
21
+ // Historical acceptance aliases retained for existing records; new Platform configuration uses
22
+ // `platform-license` for billing and Terms for product-use rules.
15
23
  billing: { effective: '2026-05-30', updated: '2026-05-30' },
16
24
  product: { effective: '2026-05-30', updated: '2026-05-30' },
17
- } as const satisfies Record<LegalDocumentKey, LegalDocumentDates>
25
+ } as const satisfies Record<LegalDocumentKey, LegalDocumentDateMetadata>
@@ -6,6 +6,17 @@ export interface LegalDocumentDates {
6
6
  updated: string
7
7
  }
8
8
 
9
+ /** A publication source awaiting activation; preparation dates are not effective dates. */
10
+ export interface PendingLegalDocumentDates {
11
+ status: 'pending'
12
+ effective: null
13
+ updated: null
14
+ }
15
+
16
+ export type LegalDocumentDateMetadata = LegalDocumentDates | PendingLegalDocumentDates
17
+
9
18
  /** The complete document set; the date table must cover every key. */
10
19
  // Kept as a type: a union of the document keys.
11
- export type LegalDocumentKey = 'terms' | 'privacy' | 'cookies' | 'services-agreement' | 'platform-license' | 'billing' | 'product'
20
+ export type LegalDocumentKey = 'terms' | 'privacy' | 'cookies' | 'services-agreement' | 'platform-license'
21
+ | 'data-processing-agreement' | 'subprocessors' | 'company-information'
22
+ | 'acceptable-use' | 'refund-cancellation' | 'service-level-agreement' | 'billing' | 'product'
@@ -33,7 +33,6 @@ export const ViableProjectFieldsSchema = {
33
33
  target: { type: 'string', enum: [...Object.values(ConnectTarget), null], nullable: true },
34
34
  origin: { ...ProjectOriginSchema, nullable: true },
35
35
  connectLlmMode: { type: 'string', enum: [...Object.values(ConnectLlm), null], nullable: true },
36
- converterLlmMode: { type: 'string', enum: [...Object.values(ConnectLlm), null], nullable: true },
37
36
  // `story: null` is a DECIDED "no gate" and must stay spellable; an absent `landing` is "never
38
37
  // decided". `at` carries no `format`: the planning registry compiles without ajv-formats.
39
38
  landing: {
@@ -37,8 +37,6 @@ export type ViableProjectFields = {
37
37
  origin?: ProjectOrigin
38
38
  /** Who performs a run's model calls on this project; absent inherits the owner's preference. */
39
39
  connectLlmMode?: ConnectLlm
40
- /** Who performs a conversion's model calls on this project; absent inherits. */
41
- converterLlmMode?: ConnectLlm
42
40
  /**
43
41
  * The landing-gate decision: which story a guest can start on the landing page.
44
42
  *
@@ -78,6 +78,10 @@ describe('viable-common - the conversion additions to the connector contract', (
78
78
  })).toBe(true)
79
79
  // A local target the connector is already attached to fetches nothing.
80
80
  expect(create({})).toBe(true)
81
+ // The caller's unattached delegated session performs the pre-card checks; a stray key is refused.
82
+ expect(create({ name: 'Deskflow', sessionId: 'session-1' })).toBe(true)
83
+ expect(create({ sessionId: '' })).toBe(false)
84
+ expect(create({ llmMode: 'local' })).toBe(false)
81
85
  expect(proceed({ decision: ConversionDecision.Extract })).toBe(true)
82
86
  expect(proceed({ decision: 'teleport' })).toBe(false)
83
87
  })
@@ -146,8 +150,8 @@ describe('viable-common - the conversion additions to the connector contract', (
146
150
  })
147
151
 
148
152
  test('the conversion routes hang under the connector base and carry no paid gate', () => {
149
- // Delegated inference is the DEFAULT for a conversion, not an experimental capability — so
150
- // unlike the delegated session, nothing here sits behind the local-LLM gate.
153
+ // A conversion carries no inference mode of its own: its model calls follow the session that
154
+ // drives it, and that session's open is the one place the local-LLM gate sits.
151
155
  expect(entrypointOf(connect.session.openDelegated).gate?.alias).toBe('test-paid-llm')
152
156
 
153
157
  const gated = [...Object.values(connect.convert), connect.inquiry.answer]