@noodleseed/one 0.173.0 → 0.175.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 (170) hide show
  1. package/dist/commands/solutions-activity.js +1 -1
  2. package/dist/commands/solutions-activity.js.map +1 -1
  3. package/dist/commands/solutions-drafts.d.ts.map +1 -1
  4. package/dist/commands/solutions-drafts.js +36 -9
  5. package/dist/commands/solutions-drafts.js.map +1 -1
  6. package/dist/commands/solutions-native-lifecycle.d.ts +5 -0
  7. package/dist/commands/solutions-native-lifecycle.d.ts.map +1 -0
  8. package/dist/commands/solutions-native-lifecycle.js +77 -0
  9. package/dist/commands/solutions-native-lifecycle.js.map +1 -0
  10. package/dist/commands/solutions-ops-args.d.ts +42 -0
  11. package/dist/commands/solutions-ops-args.d.ts.map +1 -0
  12. package/dist/commands/solutions-ops-args.js +47 -0
  13. package/dist/commands/solutions-ops-args.js.map +1 -0
  14. package/dist/commands/solutions-ops.d.ts.map +1 -1
  15. package/dist/commands/solutions-ops.js +9 -46
  16. package/dist/commands/solutions-ops.js.map +1 -1
  17. package/dist/commands/solutions-workspace.d.ts +5 -0
  18. package/dist/commands/solutions-workspace.d.ts.map +1 -0
  19. package/dist/commands/solutions-workspace.js +157 -0
  20. package/dist/commands/solutions-workspace.js.map +1 -0
  21. package/dist/commands/whatsapp-output.d.ts.map +1 -1
  22. package/dist/commands/whatsapp-output.js +20 -4
  23. package/dist/commands/whatsapp-output.js.map +1 -1
  24. package/node_modules/@noodle-borg/admission-limits/dist/public-record-admission.d.ts +16 -9
  25. package/node_modules/@noodle-borg/admission-limits/dist/public-record-admission.js +5 -2
  26. package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +9 -0
  27. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +5 -5
  28. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +9 -0
  29. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +1 -1
  30. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +13 -1
  31. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  32. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +5 -3
  33. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +5 -4
  34. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-inbox.js +6 -2
  35. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.d.ts +1 -1
  36. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +12 -0
  37. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.d.ts +7 -2
  38. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.js +14 -4
  39. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-capture.js +104 -0
  40. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-judgment.js +93 -0
  41. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-ledger.d.ts +105 -0
  42. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-ledger.js +183 -0
  43. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-model-call.js +38 -0
  44. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-parsers.d.ts +22 -0
  45. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-parsers.js +109 -0
  46. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-review.d.ts +21 -0
  47. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-review.js +44 -0
  48. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-spec.d.ts +18 -0
  49. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-spec.js +86 -0
  50. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-store.d.ts +11 -0
  51. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-store.js +18 -0
  52. package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.d.ts +20 -1
  53. package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.js +38 -2
  54. package/node_modules/@noodle-borg/assistant-gateway/dist/model-runtime.js +3 -0
  55. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +5 -0
  56. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +5 -0
  57. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -0
  58. package/node_modules/@noodle-borg/authoring/dist/interaction.d.ts +49 -0
  59. package/node_modules/@noodle-borg/authoring/dist/interaction.js +25 -0
  60. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -0
  61. package/node_modules/@noodle-borg/authoring/dist/server.js +4 -0
  62. package/node_modules/@noodle-borg/authoring/package.json +6 -1
  63. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +1 -1
  64. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-drafts.js +7 -0
  65. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-native-lifecycle.js +43 -0
  66. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-records.js +154 -0
  67. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-workspace.js +95 -0
  68. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solutions.js +4 -149
  69. package/node_modules/@noodle-borg/compiler/dist/artifact/assemble.js +2 -1
  70. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +33 -0
  71. package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +2 -1
  72. package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +2 -1
  73. package/node_modules/@noodle-borg/compiler/dist/compile.js +5 -0
  74. package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
  75. package/node_modules/@noodle-borg/compiler/dist/index.d.ts +2 -2
  76. package/node_modules/@noodle-borg/compiler/dist/index.js +1 -1
  77. package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-schema.d.ts +41 -0
  78. package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-schema.js +62 -0
  79. package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-validation.js +220 -0
  80. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +60 -4
  81. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +7 -1
  82. package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.d.ts +9 -1
  83. package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.js +58 -10
  84. package/node_modules/@noodle-borg/control-plane/dist/in-memory-atomic-state.d.ts +8 -0
  85. package/node_modules/@noodle-borg/control-plane/dist/in-memory-atomic-state.js +136 -0
  86. package/node_modules/@noodle-borg/control-plane/dist/in-memory-control-plane-store.d.ts +7 -0
  87. package/node_modules/@noodle-borg/control-plane/dist/in-memory-control-plane-store.js +16 -24
  88. package/node_modules/@noodle-borg/control-plane/dist/in-memory-organization-store.d.ts +4 -2
  89. package/node_modules/@noodle-borg/control-plane/dist/in-memory-organization-store.js +8 -6
  90. package/node_modules/@noodle-borg/control-plane/dist/mcp-subdomain-claims.d.ts +2 -1
  91. package/node_modules/@noodle-borg/control-plane/dist/mcp-subdomain-claims.js +13 -8
  92. package/node_modules/@noodle-borg/control-plane/dist/organization-agreements.d.ts +13 -4
  93. package/node_modules/@noodle-borg/control-plane/dist/organization-agreements.js +23 -7
  94. package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +2 -1
  95. package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
  96. package/node_modules/@noodle-borg/module/dist/platform-human-identity.d.ts +5 -2
  97. package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +16 -4
  98. package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +3 -6
  99. package/node_modules/@noodle-borg/runtime/dist/index.d.ts +1 -1
  100. package/node_modules/@noodle-borg/service/dist/application-activity.js +85 -63
  101. package/node_modules/@noodle-borg/service/dist/application-drafts/contracts.js +9 -0
  102. package/node_modules/@noodle-borg/service/dist/application-drafts/paths.js +2 -2
  103. package/node_modules/@noodle-borg/service/dist/application-runtime-target.js +1 -1
  104. package/node_modules/@noodle-borg/service/dist/business-information/contracts.d.ts +18 -4
  105. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-business-pages.js +3 -5
  106. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-installation-lifecycle.js +8 -6
  107. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-native-lifecycle.js +80 -0
  108. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-store.js +27 -31
  109. package/node_modules/@noodle-borg/service/dist/business-information/memory-native-retention.js +29 -0
  110. package/node_modules/@noodle-borg/service/dist/business-information/model.js +9 -2
  111. package/node_modules/@noodle-borg/service/dist/business-information/native-query.js +3 -1
  112. package/node_modules/@noodle-borg/service/dist/business-information/native-record-lifecycle.d.ts +27 -0
  113. package/node_modules/@noodle-borg/service/dist/business-information/native-record-lifecycle.js +63 -0
  114. package/node_modules/@noodle-borg/service/dist/business-information/principal-authority.d.ts +3 -1
  115. package/node_modules/@noodle-borg/service/dist/business-information/principal-authority.js +9 -4
  116. package/node_modules/@noodle-borg/service/dist/business-information/staff-authority.d.ts +16 -0
  117. package/node_modules/@noodle-borg/service/dist/business-information/staff-authority.js +60 -0
  118. package/node_modules/@noodle-borg/service/dist/business-information/workspace-permissions.js +18 -0
  119. package/node_modules/@noodle-borg/service/dist/business-information-runtime.js +1 -0
  120. package/node_modules/@noodle-borg/service/dist/business-onboarding.js +16 -3
  121. package/node_modules/@noodle-borg/service/dist/business-workspaces/contracts.d.ts +110 -0
  122. package/node_modules/@noodle-borg/service/dist/business-workspaces/contracts.js +52 -0
  123. package/node_modules/@noodle-borg/service/dist/business-workspaces/paths.js +17 -0
  124. package/node_modules/@noodle-borg/service/dist/business-workspaces/permissions.d.ts +7 -0
  125. package/node_modules/@noodle-borg/service/dist/business-workspaces/store.d.ts +102 -0
  126. package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +70 -8
  127. package/node_modules/@noodle-borg/service/dist/channels/capability-report.js +73 -0
  128. package/node_modules/@noodle-borg/service/dist/channels/collection-turn.js +232 -0
  129. package/node_modules/@noodle-borg/service/dist/channels/composition.js +1 -0
  130. package/node_modules/@noodle-borg/service/dist/channels/messaging-eligibility.js +45 -0
  131. package/node_modules/@noodle-borg/service/dist/channels/reply-buttons.js +23 -0
  132. package/node_modules/@noodle-borg/service/dist/channels/runtime.js +127 -33
  133. package/node_modules/@noodle-borg/service/dist/native-record-connector.js +19 -4
  134. package/node_modules/@noodle-borg/service/dist/routes/application-drafts.js +44 -2
  135. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +4 -0
  136. package/node_modules/@noodle-borg/service/dist/routes/business-authoring-dispatch.js +23 -0
  137. package/node_modules/@noodle-borg/service/dist/routes/business-information-access.js +39 -0
  138. package/node_modules/@noodle-borg/service/dist/routes/business-information-activity.js +1 -1
  139. package/node_modules/@noodle-borg/service/dist/routes/business-information-channels.js +2 -2
  140. package/node_modules/@noodle-borg/service/dist/routes/business-information-connections.js +10 -4
  141. package/node_modules/@noodle-borg/service/dist/routes/business-information-dispatch.js +11 -6
  142. package/node_modules/@noodle-borg/service/dist/routes/business-information-installation.js +3 -1
  143. package/node_modules/@noodle-borg/service/dist/routes/business-information-notice.js +6 -2
  144. package/node_modules/@noodle-borg/service/dist/routes/business-information-pages.js +1 -1
  145. package/node_modules/@noodle-borg/service/dist/routes/business-information-paths.js +1 -1
  146. package/node_modules/@noodle-borg/service/dist/routes/business-information-record-create.js +7 -2
  147. package/node_modules/@noodle-borg/service/dist/routes/business-information-record-reads.js +2 -2
  148. package/node_modules/@noodle-borg/service/dist/routes/business-information-records.js +332 -0
  149. package/node_modules/@noodle-borg/service/dist/routes/business-information-settings.js +6 -3
  150. package/node_modules/@noodle-borg/service/dist/routes/business-information-staff.js +9 -5
  151. package/node_modules/@noodle-borg/service/dist/routes/business-information.js +12 -356
  152. package/node_modules/@noodle-borg/service/dist/routes/business-native-lifecycle.js +46 -0
  153. package/node_modules/@noodle-borg/service/dist/routes/business-workspaces.js +119 -0
  154. package/node_modules/@noodle-borg/service/dist/routes/service-probes.js +2 -1
  155. package/node_modules/@noodle-borg/service/dist/service.js +9 -2
  156. package/node_modules/@noodle-borg/service/dist/web-capabilities.js +3 -1
  157. package/node_modules/@noodle-borg/service/package.json +2 -1
  158. package/node_modules/@noodle-borg/wire-contracts/dist/application-draft-validation.d.ts +59 -0
  159. package/node_modules/@noodle-borg/wire-contracts/dist/application-draft-validation.js +38 -0
  160. package/node_modules/@noodle-borg/wire-contracts/dist/business-information.d.ts +144 -50
  161. package/node_modules/@noodle-borg/wire-contracts/dist/business-information.js +44 -4
  162. package/node_modules/@noodle-borg/wire-contracts/dist/business-workspaces.d.ts +258 -0
  163. package/node_modules/@noodle-borg/wire-contracts/dist/business-workspaces.js +118 -1
  164. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +2 -0
  165. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +2 -0
  166. package/node_modules/@noodle-borg/wire-contracts/dist/native-record-lifecycle.d.ts +81 -0
  167. package/node_modules/@noodle-borg/wire-contracts/dist/native-record-lifecycle.js +39 -0
  168. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +26 -0
  169. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +24 -1
  170. package/package.json +2 -2
@@ -3,11 +3,11 @@ import { NativeQueryError } from '../business-information/native-query.js';
3
3
  import { CursorValidationError } from '../business-information/validation.js';
4
4
  import { parseBusinessPaging } from './business-information-request.js';
5
5
  import { activityToWire } from './business-information-wire.js';
6
- export async function sendNativeRecordActivity(res, url, scope, collection, id, store) {
6
+ export async function sendNativeRecordActivity(res, url, scope, collection, id, store, actor) {
7
7
  const paging = parseBusinessPaging(url, 100);
8
8
  if (!paging.ok)
9
9
  return sendJson(res, 400, { error: paging.error });
10
- const history = await cursorRequest(res, () => store.listActivity(scope, collection, id, paging.value));
10
+ const history = await cursorRequest(res, () => store.staff.run(scope, actor, 'records:read', () => store.listActivity(scope, collection, id, paging.value)));
11
11
  if (history === undefined)
12
12
  return;
13
13
  return sendJson(res, 200, {
@@ -0,0 +1,332 @@
1
+ import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
2
+ import { ManagedRecordCreateRequestSchema, ManagedRecordMutationRequestSchema, ManagedRecordStatusSchema, } from '@noodle-borg/wire-contracts';
3
+ import { admitBusinessMutation } from '../business-api-admission.js';
4
+ import { businessGrantAllows, PayloadValidationError, } from '../business-information/portable.js';
5
+ import { enabledCollection, idempotencyConflict, invalid, methodNotAllowed, mutationFailure, now, } from './business-information.js';
6
+ import { requireIdentity, requireInstallationPermission, resolveBusinessStaffGrant, runBusinessStaffOperation, } from './business-information-access.js';
7
+ import { loadExternalRecord, sendExternalRecordPage, sourceBindingKey, unsupportedExternalOperation, } from './business-information-external.js';
8
+ import { createNativeRecord } from './business-information-record-create.js';
9
+ import { cursorRequest, sendNativeRecordActivity } from './business-information-record-reads.js';
10
+ import { expectedRevision, mutationPermission, parseNativeRecordQuery, parseBusinessPaging as parsePaging, requestIdempotencyKey, } from './business-information-request.js';
11
+ import { collectionToWire, externalRecordToWire, recordDetailToWire, recordToWire, } from './business-information-wire.js';
12
+ export async function handleManagedRecords(req, res, url, ref, deps) {
13
+ const identity = await requireIdentity(req, res, deps);
14
+ if (identity === false)
15
+ return;
16
+ if (ref.collection === undefined)
17
+ return sendJson(res, 404, { error: 'not found' });
18
+ const collection = ref.collection;
19
+ if (ref.action === 'activity' && req.method === 'GET' && ref.recordId !== undefined) {
20
+ const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
21
+ if (authorized === undefined)
22
+ return;
23
+ const definition = enabledCollection(res, authorized.installation, collection);
24
+ if (definition === undefined)
25
+ return;
26
+ if (definition.authority.authority === 'external') {
27
+ return sendJson(res, 200, { ok: true, data: { activities: [] } });
28
+ }
29
+ return sendNativeRecordActivity(res, url, authorized.installation.scope, collection, ref.recordId, deps.store, identity.subject);
30
+ }
31
+ if (ref.action === 'export' && req.method === 'GET') {
32
+ const authorized = await requireInstallationPermission(res, ref, identity, 'records:export', deps);
33
+ if (authorized === undefined)
34
+ return;
35
+ const definition = enabledCollection(res, authorized.installation, collection);
36
+ if (definition === undefined)
37
+ return;
38
+ const paging = parsePaging(url, 500);
39
+ if (!paging.ok)
40
+ return sendJson(res, 400, { error: paging.error });
41
+ if (definition.authority.authority === 'external') {
42
+ return sendExternalRecordPage(res, authorized.installation, definition, { ...paging.value, limit: paging.value.limit ?? 500 }, deps);
43
+ }
44
+ const page = await cursorRequest(res, () => runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'records:export', () => deps.store.exportRequests({
45
+ scope: authorized.installation.scope,
46
+ collectionKey: collection,
47
+ ...paging.value,
48
+ includeDeleted: url.searchParams.get('includeDeleted') === 'true',
49
+ })));
50
+ if (page === undefined)
51
+ return;
52
+ return sendJson(res, 200, {
53
+ ok: true,
54
+ data: {
55
+ records: page.records.map((record) => ({
56
+ ...recordToWire(record),
57
+ ...(record.content ? { notes: record.content.notes } : {}),
58
+ })),
59
+ ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
60
+ },
61
+ });
62
+ }
63
+ if (ref.recordId === undefined && req.method === 'GET') {
64
+ const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
65
+ if (authorized === undefined)
66
+ return;
67
+ const definition = enabledCollection(res, authorized.installation, collection);
68
+ if (definition === undefined)
69
+ return;
70
+ const paging = parsePaging(url, 100);
71
+ if (!paging.ok)
72
+ return sendJson(res, 400, { error: paging.error });
73
+ const query = parseNativeRecordQuery(url);
74
+ if (!query.ok)
75
+ return sendJson(res, 400, { error: query.error, code: 'invalid_query' });
76
+ if (definition.authority.authority === 'external') {
77
+ if (Object.keys(query.value).length > 0)
78
+ return sendJson(res, 400, {
79
+ error: 'Field and date queries currently require a native collection.',
80
+ code: 'invalid_query',
81
+ });
82
+ return sendExternalRecordPage(res, authorized.installation, definition, paging.value, deps);
83
+ }
84
+ const statusValue = url.searchParams.get('status');
85
+ const status = statusValue === null ? undefined : ManagedRecordStatusSchema.safeParse(statusValue);
86
+ if (status !== undefined && !status.success)
87
+ return sendJson(res, 400, { error: 'invalid status' });
88
+ const assigneeSubject = url.searchParams.get('assigneeSubject') ?? undefined;
89
+ const page = await cursorRequest(res, () => runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'records:read', () => deps.store.listRequests({
90
+ scope: authorized.installation.scope,
91
+ collectionKey: collection,
92
+ ...paging.value,
93
+ ...query.value,
94
+ ...(status === undefined ? {} : { status: status.data }),
95
+ ...(assigneeSubject === undefined ? {} : { assigneeSubject }),
96
+ includeDeleted: url.searchParams.get('includeDeleted') === 'true',
97
+ })));
98
+ if (page === undefined)
99
+ return;
100
+ return sendJson(res, 200, {
101
+ ok: true,
102
+ data: {
103
+ records: page.records.map(recordToWire),
104
+ ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
105
+ },
106
+ });
107
+ }
108
+ if (ref.recordId === undefined && req.method === 'POST') {
109
+ const authorized = await requireInstallationPermission(res, ref, identity, 'records:create', deps);
110
+ if (authorized === undefined)
111
+ return;
112
+ const definition = enabledCollection(res, authorized.installation, collection);
113
+ if (definition === undefined)
114
+ return;
115
+ if (definition.authority.authority === 'external') {
116
+ return unsupportedExternalOperation(res, 'create');
117
+ }
118
+ const idempotencyKey = requestIdempotencyKey(req);
119
+ if (idempotencyKey === undefined) {
120
+ return sendJson(res, 400, { error: 'Idempotency-Key header is required (1-128 characters)' });
121
+ }
122
+ const body = await readJsonBody(req, deps.maxBody);
123
+ if (!body.ok)
124
+ return sendJson(res, body.status, { error: body.error });
125
+ const parsed = ManagedRecordCreateRequestSchema.safeParse(body.value);
126
+ if (!parsed.success)
127
+ return invalid(res, parsed.error);
128
+ const result = await createNativeRecord(res, deps, {
129
+ installation: authorized.installation,
130
+ collection,
131
+ idempotencyKey,
132
+ payload: parsed.data.payload,
133
+ origin: { kind: 'portal' },
134
+ actorSubject: identity.subject,
135
+ admit: () => admitBusinessMutation(res, authorized.scope),
136
+ });
137
+ if (result === undefined)
138
+ return;
139
+ if (result.disposition === 'conflict')
140
+ return idempotencyConflict(res);
141
+ if (result.disposition === 'paused') {
142
+ return sendJson(res, 503, {
143
+ error: 'Record creation requires an active application.',
144
+ code: 'application_unavailable',
145
+ });
146
+ }
147
+ return sendJson(res, result.disposition === 'created' ? 201 : 200, {
148
+ ok: true,
149
+ data: { record: recordToWire(result.record) },
150
+ });
151
+ }
152
+ if (ref.recordId !== undefined && req.method === 'GET') {
153
+ const recordId = ref.recordId;
154
+ const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
155
+ if (authorized === undefined)
156
+ return;
157
+ const definition = enabledCollection(res, authorized.installation, collection);
158
+ if (definition === undefined)
159
+ return;
160
+ if (definition.authority.authority === 'external') {
161
+ const loaded = await loadExternalRecord(authorized.installation, definition, ref.recordId, deps);
162
+ if (loaded === undefined)
163
+ return sendJson(res, 503, { error: 'collection source unavailable' });
164
+ if (loaded.record === undefined)
165
+ return sendJson(res, 404, { error: 'record not found' });
166
+ return sendJson(res, 200, {
167
+ ok: true,
168
+ data: {
169
+ record: externalRecordToWire(loaded.record, loaded.binding),
170
+ collection: collectionToWire(definition),
171
+ },
172
+ });
173
+ }
174
+ const record = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'records:read', () => deps.store.getRequest(authorized.installation.scope, collection, recordId));
175
+ if (record === undefined)
176
+ return sendJson(res, 404, { error: 'record not found' });
177
+ return sendJson(res, 200, {
178
+ ok: true,
179
+ data: recordDetailToWire(authorized.installation, record),
180
+ });
181
+ }
182
+ if (ref.recordId !== undefined && req.method === 'PATCH') {
183
+ const recordId = ref.recordId;
184
+ const body = await readJsonBody(req, deps.maxBody);
185
+ if (!body.ok)
186
+ return sendJson(res, body.status, { error: body.error });
187
+ const parsed = ManagedRecordMutationRequestSchema.safeParse(body.value);
188
+ if (!parsed.success)
189
+ return invalid(res, parsed.error);
190
+ const permission = mutationPermission(parsed.data.operation);
191
+ const authorized = await requireInstallationPermission(res, ref, identity, permission, deps);
192
+ if (authorized === undefined)
193
+ return;
194
+ const definition = enabledCollection(res, authorized.installation, collection);
195
+ if (definition === undefined)
196
+ return;
197
+ if (definition.authority.authority === 'external') {
198
+ return unsupportedExternalOperation(res, parsed.data.operation);
199
+ }
200
+ if (parsed.data.operation === 'migrate-schema') {
201
+ const result = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, permission, () => deps.store.migrateLegacyRequest({
202
+ scope: authorized.installation.scope,
203
+ collectionKey: collection,
204
+ id: recordId,
205
+ expectedRevision: parsed.data.expectedRevision,
206
+ actorSubject: identity.subject,
207
+ }));
208
+ if (!result.ok)
209
+ return mutationFailure(res, result);
210
+ return sendJson(res, 200, { ok: true, data: { record: recordToWire(result.record) } });
211
+ }
212
+ let operation;
213
+ if (parsed.data.operation === 'update') {
214
+ operation = {
215
+ kind: 'update',
216
+ payload: parsed.data.patch,
217
+ ...(parsed.data.unset ? { unset: parsed.data.unset } : {}),
218
+ };
219
+ }
220
+ else if (parsed.data.operation === 'assign') {
221
+ if (parsed.data.assigneeSubject !== null) {
222
+ const assigneeGrant = await resolveBusinessStaffGrant(deps, authorized.installation.scope, parsed.data.assigneeSubject);
223
+ if (assigneeGrant === undefined ||
224
+ assigneeGrant.revokedAt !== undefined ||
225
+ !businessGrantAllows(assigneeGrant, 'records:assign')) {
226
+ return sendJson(res, 422, {
227
+ error: 'assignee must have an active operator grant',
228
+ code: 'invalid_assignee',
229
+ });
230
+ }
231
+ }
232
+ operation = {
233
+ kind: 'assign',
234
+ assigneeSubject: parsed.data.assigneeSubject ?? undefined,
235
+ };
236
+ }
237
+ else if (parsed.data.operation === 'set-status') {
238
+ operation = { kind: 'set_status', status: parsed.data.status };
239
+ }
240
+ else {
241
+ operation = { kind: 'add_note', note: parsed.data.note };
242
+ }
243
+ let result;
244
+ try {
245
+ result = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, permission, () => deps.store.mutateRequest({
246
+ scope: authorized.installation.scope,
247
+ collectionKey: collection,
248
+ id: recordId,
249
+ expectedRevision: parsed.data.expectedRevision,
250
+ actorSubject: identity.subject,
251
+ operation,
252
+ }));
253
+ }
254
+ catch (error) {
255
+ if (error instanceof PayloadValidationError) {
256
+ return sendJson(res, 400, { error: error.message, code: error.code });
257
+ }
258
+ throw error;
259
+ }
260
+ if (!result.ok)
261
+ return mutationFailure(res, result);
262
+ return sendJson(res, 200, { ok: true, data: { record: recordToWire(result.record) } });
263
+ }
264
+ if (ref.recordId !== undefined && req.method === 'DELETE') {
265
+ const recordId = ref.recordId;
266
+ const authorized = await requireInstallationPermission(res, ref, identity, 'records:delete', deps);
267
+ if (authorized === undefined)
268
+ return;
269
+ const definition = enabledCollection(res, authorized.installation, collection);
270
+ if (definition === undefined)
271
+ return;
272
+ const body = await readJsonBody(req, deps.maxBody);
273
+ if (!body.ok)
274
+ return sendJson(res, body.status, { error: body.error });
275
+ const revision = expectedRevision(body.value);
276
+ if (revision === undefined) {
277
+ return sendJson(res, 400, { error: 'expectedRevision must be a positive integer' });
278
+ }
279
+ if (definition.authority.authority === 'external') {
280
+ const loaded = await loadExternalRecord(authorized.installation, definition, ref.recordId, deps);
281
+ if (loaded === undefined)
282
+ return sendJson(res, 503, { error: 'collection source unavailable' });
283
+ if (loaded.record === undefined)
284
+ return sendJson(res, 404, { error: 'record not found' });
285
+ const { record } = loaded;
286
+ if (record.revision !== revision) {
287
+ return sendJson(res, 409, {
288
+ error: 'record revision conflict',
289
+ code: 'revision_conflict',
290
+ currentRevision: record.revision,
291
+ });
292
+ }
293
+ await deps.sourceStore?.suppressExternalRecord({
294
+ ...sourceBindingKey(authorized.installation, definition),
295
+ sourceId: record.source.id,
296
+ reason: 'customer_request',
297
+ now: now(deps),
298
+ });
299
+ const deletedAt = now(deps).toISOString();
300
+ return sendJson(res, 200, {
301
+ ok: true,
302
+ data: {
303
+ recordId: record.id,
304
+ authority: 'external',
305
+ disposition: 'suppressed',
306
+ deletedAt,
307
+ },
308
+ });
309
+ }
310
+ const result = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'records:delete', () => deps.store.deleteRequest({
311
+ scope: authorized.installation.scope,
312
+ collectionKey: collection,
313
+ id: recordId,
314
+ expectedRevision: revision,
315
+ actorSubject: identity.subject,
316
+ reason: 'customer_request',
317
+ }));
318
+ if (!result.ok)
319
+ return mutationFailure(res, result);
320
+ return sendJson(res, 200, {
321
+ ok: true,
322
+ data: {
323
+ recordId: result.record.id,
324
+ authority: 'native',
325
+ disposition: 'deleted',
326
+ deletedAt: result.record.deletedAt,
327
+ },
328
+ });
329
+ }
330
+ return methodNotAllowed(res);
331
+ }
332
+ //# sourceMappingURL=business-information-records.js.map
@@ -26,10 +26,13 @@ export async function handleBusinessSettings(req, res, ref, deps) {
26
26
  res.setHeader('cache-control', 'private, no-store');
27
27
  try {
28
28
  if (req.method === 'GET') {
29
- await settings.initialize(target);
29
+ const data = await deps.store.staff.run(authorized.scope, identity.subject, 'records:read', async () => {
30
+ await settings.initialize(target);
31
+ return settings.read(target, canEdit);
32
+ });
30
33
  return sendJson(res, 200, ApplicationSettingsResponseSchema.parse({
31
34
  ok: true,
32
- data: await settings.read(target, canEdit),
35
+ data,
33
36
  }));
34
37
  }
35
38
  const body = await readJsonBody(req, deps.maxBody);
@@ -39,7 +42,7 @@ export async function handleBusinessSettings(req, res, ref, deps) {
39
42
  if (!parsed.success)
40
43
  return sendJson(res, 400, { code: 'settings_invalid', error: 'Invalid settings update.' });
41
44
  const registry = deps.registry;
42
- const data = await settings.save(target, parsed.data, identity.subject, registry ? () => resolveSettingsTargets(registry, target.scope, deps.store) : undefined);
45
+ const data = await deps.store.staff.run(authorized.scope, identity.subject, 'installation:administer', () => settings.save(target, parsed.data, identity.subject, registry ? () => resolveSettingsTargets(registry, target.scope, deps.store) : undefined));
43
46
  await deps.audit?.emit({
44
47
  eventType: 'config.variable.business_settings_changed',
45
48
  org: authorized.scope.org,
@@ -21,7 +21,13 @@ export async function handleMySolutionInstallations(req, res, deps) {
21
21
  return sendJson(res, 400, { error: 'cursor is invalid' });
22
22
  }
23
23
  const limit = paging.value.limit ?? 100;
24
- const joined = [...(await deps.store.listInstallationsForSubject(identity.subject))]
24
+ const current = [];
25
+ for (const { installation } of await deps.store.listInstallationsForSubject(identity.subject)) {
26
+ const grant = await deps.store.staff.resolve(installation.scope, identity.subject);
27
+ if (grant && !grant.revokedAt)
28
+ current.push({ installation, grant });
29
+ }
30
+ const joined = current
25
31
  .sort((left, right) => compareDiscoveryRows(left, right))
26
32
  .filter((item) => after === undefined || discoveryOrderKey(item) > after)
27
33
  .slice(0, limit + 1);
@@ -201,13 +207,11 @@ export async function handleEligibleBusinessAssignees(req, res, ref, deps) {
201
207
  const authorized = await requireInstallationPermission(res, ref, identity, 'records:assign', deps);
202
208
  if (authorized === undefined)
203
209
  return;
204
- const grants = await deps.store.listEligibleAssignees(authorized.scope);
210
+ const assignees = await deps.store.listEligibleAssignees(authorized.scope, identity.subject);
205
211
  sendJson(res, 200, {
206
212
  ok: true,
207
213
  data: {
208
- assignees: grants
209
- .map((grant) => ({ subject: grant.subject, email: grant.email, role: grant.role }))
210
- .slice(0, 100),
214
+ assignees: assignees.slice(0, 100),
211
215
  },
212
216
  });
213
217
  }