@aopslab/aops-server 0.1.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 (185) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +21 -0
  4. package/aops-server-runtime.json +11 -0
  5. package/build/client/_app/immutable/chunks/2_TffmqH.js +1 -0
  6. package/build/client/_app/immutable/chunks/3DOfIKW0.js +1 -0
  7. package/build/client/_app/immutable/chunks/B2EkC-sy.js +1 -0
  8. package/build/client/_app/immutable/chunks/CMkIcmi1.js +2 -0
  9. package/build/client/_app/immutable/chunks/rqnVXeyB.js +1 -0
  10. package/build/client/_app/immutable/chunks/sZYZbj9W.js +1 -0
  11. package/build/client/_app/immutable/chunks/wbPk3Yxo.js +81 -0
  12. package/build/client/_app/immutable/entry/app.C7STLTX8.js +2 -0
  13. package/build/client/_app/immutable/entry/start.BC94hOaC.js +1 -0
  14. package/build/client/_app/immutable/nodes/0.BYBfjxZ5.js +1 -0
  15. package/build/client/_app/immutable/nodes/1.BNXFXajG.js +1 -0
  16. package/build/client/_app/version.json +1 -0
  17. package/build/env.js +70 -0
  18. package/build/env.js.map +1 -0
  19. package/build/handler.js +16 -0
  20. package/build/handler.js.map +1 -0
  21. package/build/index.js +139 -0
  22. package/build/index.js.map +1 -0
  23. package/build/server/chunks/0-RfB6m2nq.js +7 -0
  24. package/build/server/chunks/0-RfB6m2nq.js.map +1 -0
  25. package/build/server/chunks/1-_gg8Q0UT.js +9 -0
  26. package/build/server/chunks/1-_gg8Q0UT.js.map +1 -0
  27. package/build/server/chunks/_server.ts-6276EQIF.js +44 -0
  28. package/build/server/chunks/_server.ts-6276EQIF.js.map +1 -0
  29. package/build/server/chunks/_server.ts-B59WFQzX.js +174 -0
  30. package/build/server/chunks/_server.ts-B59WFQzX.js.map +1 -0
  31. package/build/server/chunks/_server.ts-BauJsLZI.js +40 -0
  32. package/build/server/chunks/_server.ts-BauJsLZI.js.map +1 -0
  33. package/build/server/chunks/_server.ts-BnGzJw2-.js +81 -0
  34. package/build/server/chunks/_server.ts-BnGzJw2-.js.map +1 -0
  35. package/build/server/chunks/_server.ts-CP7qM24V.js +34 -0
  36. package/build/server/chunks/_server.ts-CP7qM24V.js.map +1 -0
  37. package/build/server/chunks/_server.ts-Cbq_u99d.js +9 -0
  38. package/build/server/chunks/_server.ts-Cbq_u99d.js.map +1 -0
  39. package/build/server/chunks/_server.ts-Ch8fPT-o.js +92 -0
  40. package/build/server/chunks/_server.ts-Ch8fPT-o.js.map +1 -0
  41. package/build/server/chunks/_server.ts-Cv-yaXZI.js +7 -0
  42. package/build/server/chunks/_server.ts-Cv-yaXZI.js.map +1 -0
  43. package/build/server/chunks/_server.ts-D6lBwOON.js +34 -0
  44. package/build/server/chunks/_server.ts-D6lBwOON.js.map +1 -0
  45. package/build/server/chunks/_server.ts-D84K3pp9.js +11 -0
  46. package/build/server/chunks/_server.ts-D84K3pp9.js.map +1 -0
  47. package/build/server/chunks/_server.ts-DNzU1ToA.js +277 -0
  48. package/build/server/chunks/_server.ts-DNzU1ToA.js.map +1 -0
  49. package/build/server/chunks/_server.ts-DQQnw77t.js +17 -0
  50. package/build/server/chunks/_server.ts-DQQnw77t.js.map +1 -0
  51. package/build/server/chunks/_server.ts-DSxr5l15.js +18 -0
  52. package/build/server/chunks/_server.ts-DSxr5l15.js.map +1 -0
  53. package/build/server/chunks/_server.ts-DZYokvuR.js +80 -0
  54. package/build/server/chunks/_server.ts-DZYokvuR.js.map +1 -0
  55. package/build/server/chunks/_server.ts-Dcqwpz2t.js +144 -0
  56. package/build/server/chunks/_server.ts-Dcqwpz2t.js.map +1 -0
  57. package/build/server/chunks/_server.ts-Di9VJnF4.js +81 -0
  58. package/build/server/chunks/_server.ts-Di9VJnF4.js.map +1 -0
  59. package/build/server/chunks/_server.ts-FBEHFT8b.js +33 -0
  60. package/build/server/chunks/_server.ts-FBEHFT8b.js.map +1 -0
  61. package/build/server/chunks/_server.ts-gZytCuiJ.js +10 -0
  62. package/build/server/chunks/_server.ts-gZytCuiJ.js.map +1 -0
  63. package/build/server/chunks/_server.ts-tNULLyEO.js +261 -0
  64. package/build/server/chunks/_server.ts-tNULLyEO.js.map +1 -0
  65. package/build/server/chunks/agent-gateway-runtime-DTZ4SxaC.js +2598 -0
  66. package/build/server/chunks/agent-gateway-runtime-DTZ4SxaC.js.map +1 -0
  67. package/build/server/chunks/aops-runtime-config-wpVmg7T-.js +179 -0
  68. package/build/server/chunks/aops-runtime-config-wpVmg7T-.js.map +1 -0
  69. package/build/server/chunks/discovery-D9JnSgfu.js +894 -0
  70. package/build/server/chunks/discovery-D9JnSgfu.js.map +1 -0
  71. package/build/server/chunks/error.svelte-DgUOqi-L.js +45 -0
  72. package/build/server/chunks/error.svelte-DgUOqi-L.js.map +1 -0
  73. package/build/server/chunks/handler-BN3-p5rP.js +1733 -0
  74. package/build/server/chunks/handler-BN3-p5rP.js.map +1 -0
  75. package/build/server/chunks/hooks.server-C4wxiH5m.js +52 -0
  76. package/build/server/chunks/hooks.server-C4wxiH5m.js.map +1 -0
  77. package/build/server/chunks/index.js-nbqdVuWC.js +10698 -0
  78. package/build/server/chunks/index.js-nbqdVuWC.js.map +1 -0
  79. package/build/server/chunks/manifest.js-_Re-Wj-y.js +167 -0
  80. package/build/server/chunks/manifest.js-_Re-Wj-y.js.map +1 -0
  81. package/build/server/chunks/plugin-error-normalize-Bq14Qbz3.js +297 -0
  82. package/build/server/chunks/plugin-error-normalize-Bq14Qbz3.js.map +1 -0
  83. package/build/server/chunks/project-alias-DqE2v58Q.js +29 -0
  84. package/build/server/chunks/project-alias-DqE2v58Q.js.map +1 -0
  85. package/build/server/chunks/registry-5amIZito.js +289 -0
  86. package/build/server/chunks/registry-5amIZito.js.map +1 -0
  87. package/build/server/chunks/request-base-url-BDdrsaga.js +24 -0
  88. package/build/server/chunks/request-base-url-BDdrsaga.js.map +1 -0
  89. package/build/server/chunks/request-context-tNeFQjgb.js +422 -0
  90. package/build/server/chunks/request-context-tNeFQjgb.js.map +1 -0
  91. package/build/server/chunks/xf-result-BRDjvftr.js +17 -0
  92. package/build/server/chunks/xf-result-BRDjvftr.js.map +1 -0
  93. package/build/shims.js +33 -0
  94. package/build/shims.js.map +1 -0
  95. package/cockpit/.vite/manifest.json +198 -0
  96. package/cockpit/assets/dm-sans-latin-400-normal-BwCSEQnW.woff +0 -0
  97. package/cockpit/assets/dm-sans-latin-400-normal-CW0RaeGs.woff2 +0 -0
  98. package/cockpit/assets/dm-sans-latin-500-normal-B9HHJjqV.woff2 +0 -0
  99. package/cockpit/assets/dm-sans-latin-500-normal-Dr3UlScf.woff +0 -0
  100. package/cockpit/assets/dm-sans-latin-700-normal-CUSSCpQX.woff +0 -0
  101. package/cockpit/assets/dm-sans-latin-700-normal-DvUfVpUG.woff2 +0 -0
  102. package/cockpit/assets/dm-sans-latin-ext-400-normal-BjWJ59Pq.woff +0 -0
  103. package/cockpit/assets/dm-sans-latin-ext-400-normal-BtiwyxMk.woff2 +0 -0
  104. package/cockpit/assets/dm-sans-latin-ext-500-normal-BJfUCQsA.woff2 +0 -0
  105. package/cockpit/assets/dm-sans-latin-ext-500-normal-DR84L5F-.woff +0 -0
  106. package/cockpit/assets/dm-sans-latin-ext-700-normal-BLI3TTWz.woff +0 -0
  107. package/cockpit/assets/dm-sans-latin-ext-700-normal-CJIcxD6K.woff2 +0 -0
  108. package/cockpit/assets/index-DDswO4B1.css +1 -0
  109. package/cockpit/assets/index-bovtnZ98.js +18 -0
  110. package/cockpit/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  111. package/cockpit/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  112. package/cockpit/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  113. package/cockpit/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  114. package/cockpit/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  115. package/cockpit/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  116. package/cockpit/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  117. package/cockpit/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  118. package/cockpit/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  119. package/cockpit/assets/outfit-latin-400-normal-BGsTXAXT.woff2 +0 -0
  120. package/cockpit/assets/outfit-latin-400-normal-DMwTpYkH.woff +0 -0
  121. package/cockpit/assets/outfit-latin-600-normal-B7SfZ07L.woff2 +0 -0
  122. package/cockpit/assets/outfit-latin-600-normal-BEfTtDA7.woff +0 -0
  123. package/cockpit/assets/outfit-latin-ext-400-normal-5tcqmc2S.woff2 +0 -0
  124. package/cockpit/assets/outfit-latin-ext-400-normal-DHm7mdGe.woff +0 -0
  125. package/cockpit/assets/outfit-latin-ext-600-normal-B85nYjL1.woff2 +0 -0
  126. package/cockpit/assets/outfit-latin-ext-600-normal-CWJcPgd7.woff +0 -0
  127. package/cockpit/assets/unbounded-cyrillic-600-normal-B-s5c2Kn.woff +0 -0
  128. package/cockpit/assets/unbounded-cyrillic-600-normal-DW6uzIMB.woff2 +0 -0
  129. package/cockpit/assets/unbounded-latin-600-normal-BPdB_PEv.woff +0 -0
  130. package/cockpit/assets/unbounded-latin-600-normal-oRSANpZr.woff2 +0 -0
  131. package/cockpit/assets/unbounded-latin-ext-600-normal-0iZLtTDV.woff2 +0 -0
  132. package/cockpit/assets/unbounded-latin-ext-600-normal-ClMPf-bu.woff +0 -0
  133. package/cockpit/assets/unbounded-vietnamese-600-normal-BzfrgZ_C.woff +0 -0
  134. package/cockpit/assets/unbounded-vietnamese-600-normal-Cx2bTSHN.woff2 +0 -0
  135. package/cockpit/community.module-inventory.json +2389 -0
  136. package/cockpit/index.html +14 -0
  137. package/community-postgres.json +13 -0
  138. package/migrations/agentspace/0000_thin_carlie_cooper.sql +764 -0
  139. package/migrations/agentspace/meta/0000_snapshot.json +6885 -0
  140. package/migrations/agentspace/meta/_journal.json +13 -0
  141. package/migrations/chatv3/0000_dry_ego.sql +218 -0
  142. package/migrations/chatv3/0001_nosy_obadiah_stane.sql +35 -0
  143. package/migrations/chatv3/0002_narrow_darkhawk.sql +4 -0
  144. package/migrations/chatv3/0003_damp_zarek.sql +2 -0
  145. package/migrations/chatv3/0004_nervous_martin_li.sql +60 -0
  146. package/migrations/chatv3/0005_chatv3_server_encrypted_mode.sql +1 -0
  147. package/migrations/chatv3/meta/0000_snapshot.json +1887 -0
  148. package/migrations/chatv3/meta/0001_snapshot.json +2175 -0
  149. package/migrations/chatv3/meta/0002_snapshot.json +2229 -0
  150. package/migrations/chatv3/meta/0003_snapshot.json +2241 -0
  151. package/migrations/chatv3/meta/0004_snapshot.json +2746 -0
  152. package/migrations/chatv3/meta/_journal.json +48 -0
  153. package/migrations/docman/0000_curvy_valeria_richards.sql +257 -0
  154. package/migrations/docman/0001_massive_risque.sql +2 -0
  155. package/migrations/docman/0002_simple_logan.sql +50 -0
  156. package/migrations/docman/0003_quick_index.sql +45 -0
  157. package/migrations/docman/0004_known_summary.sql +9 -0
  158. package/migrations/docman/0005_vector_signal.sql +9 -0
  159. package/migrations/docman/0006_document_version_current_invariant.sql +15 -0
  160. package/migrations/docman/meta/_journal.json +55 -0
  161. package/migrations/projectman/0000_smooth_klaw.sql +257 -0
  162. package/migrations/projectman/0001_worthless_zodiak.sql +9 -0
  163. package/migrations/projectman/0002_board_rename.sql +108 -0
  164. package/migrations/projectman/0003_review_request.sql +62 -0
  165. package/migrations/projectman/0004_review_request_idempotency.sql +3 -0
  166. package/migrations/projectman/0005_allow_multi_sprint_per_task.sql +1 -0
  167. package/migrations/projectman/meta/_journal.json +48 -0
  168. package/migrations/sys/0000_loose_praxagora.sql +31 -0
  169. package/migrations/sys/0001_awesome_ronan.sql +2 -0
  170. package/migrations/sys/0002_steady_maximus.sql +2 -0
  171. package/migrations/sys/0003_escalating_rate_limiter.sql +2 -0
  172. package/migrations/sys/0004_rate_limiter_timestamptz.sql +22 -0
  173. package/migrations/sys/0005_sys_counters.sql +21 -0
  174. package/migrations/sys/meta/_journal.json +48 -0
  175. package/npm-shrinkwrap.json +4375 -0
  176. package/package.json +62 -0
  177. package/runtime/agentspace-host-adapter.mjs +64 -0
  178. package/runtime/agentspace-tooling.mjs +389 -0
  179. package/runtime/docman-host-adapter.mjs +177 -0
  180. package/runtime/docman-policy.mjs +450 -0
  181. package/runtime/docman-tooling.mjs +20 -0
  182. package/runtime/projectman-host-adapter.mjs +597 -0
  183. package/runtime/scope-context.mjs +99 -0
  184. package/scripts/community-host.mjs +626 -0
  185. package/scripts/community-migration-policy-v1.json +296 -0
@@ -0,0 +1,177 @@
1
+ // @ts-nocheck
2
+ import {
3
+ createDocmanPlugin as createBaseDocmanPlugin,
4
+ } from '@aopslab/domain-host-plugin-docman';
5
+ import {
6
+ clearDocmanKitEnvConfigCache,
7
+ clearDocmanKitOperationCaches,
8
+ parseDocmanToolInput,
9
+ runDocmanKitOperationByTypedId,
10
+ } from '@aopslab/domain-kit-docman';
11
+ import { runAgentspaceKitOperationByTypedId } from '@aopslab/domain-kit-agentspace';
12
+ import {
13
+ DEFAULT_DOCMAN_SCOPE_ID,
14
+ applyDocmanRuntimeEnv,
15
+ resolveDocmanRuntimeConfig,
16
+ } from '@aopslab/domain-runtime-config-docman';
17
+ import {
18
+ filterDocmanHostRouteProjection,
19
+ isDocmanOperationAllowed,
20
+ sanitizeDocmanOperationInput,
21
+ sanitizeDocmanOperationOutput,
22
+ } from './docman-policy.mjs';
23
+ import {
24
+ createAgentspaceScopeResolver,
25
+ normalizeScopeResolution,
26
+ toNonEmptyString,
27
+ } from './scope-context.mjs';
28
+
29
+ let docmanHostedRuntimeEnvReady = false;
30
+
31
+ function toRecord(value) {
32
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
33
+ return value;
34
+ }
35
+
36
+ function unwrapData(value) {
37
+ const record = toRecord(value);
38
+ if (Object.prototype.hasOwnProperty.call(record, 'data')) {
39
+ return record.data;
40
+ }
41
+ return value;
42
+ }
43
+
44
+ function toResultArray(value) {
45
+ const data = unwrapData(value);
46
+ if (Array.isArray(data)) return data;
47
+ const record = toRecord(data);
48
+ if (Array.isArray(record.items)) return record.items;
49
+ return [];
50
+ }
51
+
52
+ function normalizeDocmanScopeId(value) {
53
+ const normalized = toNonEmptyString(value);
54
+ if (!normalized) return undefined;
55
+ return normalized.toLowerCase() === 'default' ? DEFAULT_DOCMAN_SCOPE_ID : normalized;
56
+ }
57
+
58
+ const docmanScopeResolver = createAgentspaceScopeResolver({
59
+ runAgentspaceOperation: runAgentspaceKitOperationByTypedId,
60
+ normalizeScopeId: normalizeDocmanScopeId,
61
+ });
62
+
63
+ function ensureDocmanHostedRuntimeEnv(options = {}) {
64
+ if (docmanHostedRuntimeEnvReady) return;
65
+
66
+ clearDocmanKitEnvConfigCache();
67
+ clearDocmanKitOperationCaches();
68
+
69
+ const runtime = resolveDocmanRuntimeConfig(
70
+ {
71
+ scopeId: normalizeDocmanScopeId(options.defaultScopeId),
72
+ },
73
+ process.env,
74
+ );
75
+
76
+ if (runtime.repoDialect !== 'pg') {
77
+ throw new Error('community_docman_postgresql_runtime_required');
78
+ }
79
+
80
+ applyDocmanRuntimeEnv(
81
+ {
82
+ runtimeMode: runtime.runtimeMode,
83
+ repoUrl: runtime.repoUrl,
84
+ repoDialect: runtime.repoDialect,
85
+ scopeId: runtime.scopeId,
86
+ },
87
+ process.env,
88
+ );
89
+
90
+ if (process.env.AOPS_DB_BOOTSTRAP_MODE !== 'explicit') {
91
+ throw new Error('community_strict_bootstrap_mode_required');
92
+ }
93
+
94
+ clearDocmanKitEnvConfigCache();
95
+ clearDocmanKitOperationCaches();
96
+ docmanHostedRuntimeEnvReady = true;
97
+ }
98
+
99
+ async function loadDocumentLinkById(linkId) {
100
+ const normalizedLinkId = typeof linkId === 'string' ? linkId.trim() : '';
101
+ if (!normalizedLinkId) return null;
102
+ const result = await runDocmanKitOperationByTypedId('document-section-link.get', {
103
+ id: normalizedLinkId,
104
+ });
105
+ return result?.item ?? result ?? null;
106
+ }
107
+
108
+ async function resolveDocmanRequestScope(requestContext = {}, options = {}) {
109
+ const resolved = await docmanScopeResolver.resolveRequestScope(requestContext, options);
110
+ return {
111
+ projectId: resolved.projectId,
112
+ scopeId: resolved.scopeId ?? DEFAULT_DOCMAN_SCOPE_ID,
113
+ scopeResolution: resolved.scopeResolution ?? normalizeScopeResolution(requestContext.scopeResolution),
114
+ };
115
+ }
116
+
117
+ export function createDocmanPlugin(options = {}) {
118
+ ensureDocmanHostedRuntimeEnv(options);
119
+
120
+ const basePlugin = createBaseDocmanPlugin({
121
+ ...options,
122
+ defaultScopeId: options.defaultScopeId,
123
+ runner: async (operationId, input) => {
124
+ if (!isDocmanOperationAllowed(operationId)) {
125
+ throw new Error(`operation_route_not_found:docman:${operationId}`);
126
+ }
127
+
128
+ const sanitizedInput = await sanitizeDocmanOperationInput(operationId, input, {
129
+ loadDocumentLinkById,
130
+ });
131
+ const result = await runDocmanKitOperationByTypedId(operationId, sanitizedInput);
132
+ return sanitizeDocmanOperationOutput(operationId, result);
133
+ },
134
+ });
135
+
136
+ const filteredRoutes = filterDocmanHostRouteProjection(basePlugin.manifest.routes);
137
+
138
+ return {
139
+ ...basePlugin,
140
+ manifest: {
141
+ ...basePlugin.manifest,
142
+ routes: Array.isArray(filteredRoutes) ? filteredRoutes : filteredRoutes.routes ?? [],
143
+ meta: {
144
+ ...(basePlugin.manifest?.meta ?? {}),
145
+ adapter: 'aops-docman-policy-wrapper',
146
+ policy: 'root-or-section-pages-no-page-nesting',
147
+ },
148
+ },
149
+ execute: async (args) => {
150
+ const operationId = args?.match?.route?.operation;
151
+ if (!isDocmanOperationAllowed(operationId)) {
152
+ throw new Error(`operation_route_not_found:docman:${operationId}`);
153
+ }
154
+ const baseRoute = basePlugin.manifest.routes.find((route) => route.operation === operationId)
155
+ ?? args?.match?.route;
156
+ const inputBase =
157
+ typeof baseRoute?.buildInput === 'function'
158
+ ? baseRoute.buildInput(args?.request, args?.match?.params ?? {})
159
+ : {};
160
+ const explicitScopeId =
161
+ typeof inputBase?.scopeId === 'string' && inputBase.scopeId.trim().length > 0
162
+ ? inputBase.scopeId
163
+ : undefined;
164
+ const requestScope = await resolveDocmanRequestScope(args?.request?.context, options);
165
+ const typedInput = parseDocmanToolInput(operationId, {
166
+ ...inputBase,
167
+ scopeId: explicitScopeId ?? requestScope.scopeId,
168
+ ...(requestScope.scopeResolution ? { scopeResolution: requestScope.scopeResolution } : {}),
169
+ });
170
+ const sanitizedInput = await sanitizeDocmanOperationInput(operationId, typedInput, {
171
+ loadDocumentLinkById,
172
+ });
173
+ const result = await runDocmanKitOperationByTypedId(operationId, sanitizedInput);
174
+ return sanitizeDocmanOperationOutput(operationId, result);
175
+ },
176
+ };
177
+ }
@@ -0,0 +1,450 @@
1
+ // @ts-nocheck
2
+ const DOCMAN_CRUD_ENTITIES = Object.freeze([
3
+ 'document',
4
+ 'document-group',
5
+ 'document-version',
6
+ 'section',
7
+ 'page',
8
+ 'page-version',
9
+ 'document-section-link',
10
+ 'section-page-link',
11
+ 'snippet',
12
+ 'page-snippet-link',
13
+ 'asset',
14
+ 'asset-version',
15
+ 'embed',
16
+ 'page-embed-link',
17
+ ]);
18
+
19
+ const DOCMAN_CRUD_KINDS = Object.freeze(['list', 'get', 'create', 'update', 'delete']);
20
+
21
+ const DOCMAN_CUSTOM_OPERATION_IDS = Object.freeze([
22
+ 'document.delete.safe',
23
+ 'document-version.delete.safe',
24
+ 'document-version.import-headings',
25
+ 'document-version.set-current',
26
+ 'document.compose.index',
27
+ 'document.index.build',
28
+ 'document.index.get',
29
+ 'document.summary.build',
30
+ 'document.summary.get',
31
+ 'document.search',
32
+ 'document.scope.search',
33
+ 'document.answer-pack',
34
+ 'document.compose.fetch',
35
+ 'document.publish.materialize',
36
+ 'document-section-link.usage.list',
37
+ ]);
38
+
39
+ const DOCMAN_ALLOWED_OPERATION_IDS = Object.freeze([
40
+ ...DOCMAN_CRUD_ENTITIES.flatMap((entity) => DOCMAN_CRUD_KINDS.map((kind) => `${entity}.${kind}`)),
41
+ ...DOCMAN_CUSTOM_OPERATION_IDS,
42
+ ]);
43
+
44
+ const SECTION_CONTENT_FIELDS = Object.freeze([
45
+ 'description',
46
+ 'descriptionMl',
47
+ 'summary',
48
+ 'releaseNotes',
49
+ 'releaseNotesMl',
50
+ 'status',
51
+ 'intro',
52
+ 'directives',
53
+ 'meta',
54
+ ]);
55
+
56
+ function toRecord(value) {
57
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
58
+ return value;
59
+ }
60
+
61
+ function normalizeNonEmpty(value) {
62
+ if (typeof value !== 'string') return '';
63
+ const trimmed = value.trim();
64
+ return trimmed.length > 0 ? trimmed : '';
65
+ }
66
+
67
+ function hasOwn(record, key) {
68
+ return Object.prototype.hasOwnProperty.call(record, key);
69
+ }
70
+
71
+ function stripKeys(record, keys) {
72
+ const next = { ...toRecord(record) };
73
+ for (const key of keys) {
74
+ delete next[key];
75
+ }
76
+ return next;
77
+ }
78
+
79
+ function inferDocumentLinkKind(link) {
80
+ const record = toRecord(link);
81
+ const kind = normalizeNonEmpty(record.kind).toLowerCase();
82
+ if (kind === 'section' || kind === 'page') return kind;
83
+ if (normalizeNonEmpty(record.pageVersionId)) return 'page';
84
+ if (normalizeNonEmpty(record.sectionId)) return 'section';
85
+ return '';
86
+ }
87
+
88
+ function toDepth(value) {
89
+ const parsed = Number(value);
90
+ if (!Number.isFinite(parsed) || parsed < 0) return 0;
91
+ return Math.floor(parsed);
92
+ }
93
+
94
+ function getDocumentLinkIdentity(record) {
95
+ const row = toRecord(record);
96
+ return normalizeNonEmpty(row.id) || normalizeNonEmpty(row.linkId);
97
+ }
98
+
99
+ function toAllowedRouteList(routeProjection) {
100
+ const rawRoutes = Array.isArray(routeProjection)
101
+ ? routeProjection
102
+ : Array.isArray(routeProjection?.routes)
103
+ ? routeProjection.routes
104
+ : [];
105
+ return rawRoutes.filter((route) => isDocmanOperationAllowed(route?.operation));
106
+ }
107
+
108
+ function sanitizeSectionPayload(record, field) {
109
+ const input = toRecord(record);
110
+ return {
111
+ ...input,
112
+ [field]: stripKeys(input[field], SECTION_CONTENT_FIELDS),
113
+ };
114
+ }
115
+
116
+ function sanitizeSectionPageLinkPayload(record, field) {
117
+ const input = toRecord(record);
118
+ const data = { ...toRecord(input[field]) };
119
+ const sectionId = normalizeNonEmpty(data.sectionId);
120
+ const parentLinkId = normalizeNonEmpty(data.parentLinkId);
121
+
122
+ if (parentLinkId) {
123
+ throw new Error('Pages cannot be nested under another page.');
124
+ }
125
+
126
+ if (sectionId) {
127
+ data.sectionId = sectionId;
128
+ }
129
+ delete data.parentLinkId;
130
+ delete data.depth;
131
+
132
+ return {
133
+ ...input,
134
+ [field]: data,
135
+ };
136
+ }
137
+
138
+ async function sanitizeDocumentSectionLinkCreateInput(record, loadDocumentLinkById) {
139
+ const input = toRecord(record);
140
+ const data = { ...toRecord(input.data) };
141
+ const kind = inferDocumentLinkKind(data);
142
+ const parentLinkId = normalizeNonEmpty(data.parentLinkId);
143
+
144
+ let parentLink = null;
145
+ if (parentLinkId) {
146
+ if (typeof loadDocumentLinkById !== 'function') {
147
+ throw new Error('docman_parent_lookup_unavailable');
148
+ }
149
+ parentLink = await loadDocumentLinkById(parentLinkId);
150
+ if (!parentLink?.id) {
151
+ throw new Error('Selected parent section could not be resolved.');
152
+ }
153
+ if (inferDocumentLinkKind(parentLink) !== 'section') {
154
+ throw new Error(
155
+ kind === 'page'
156
+ ? 'Pages cannot be nested under another page.'
157
+ : 'Sections can only be nested under other sections.'
158
+ );
159
+ }
160
+ }
161
+
162
+ delete data.depth;
163
+ if (parentLinkId) {
164
+ data.parentLinkId = parentLinkId;
165
+ data.depth = toDepth(parentLink?.depth) + 1;
166
+ } else {
167
+ delete data.parentLinkId;
168
+ data.depth = 0;
169
+ }
170
+
171
+ return {
172
+ ...input,
173
+ data,
174
+ };
175
+ }
176
+
177
+ async function sanitizeDocumentSectionLinkUpdateInput(record, loadDocumentLinkById) {
178
+ const input = toRecord(record);
179
+ const patch = { ...toRecord(input.patch) };
180
+ const hasParentUpdate = hasOwn(patch, 'parentLinkId');
181
+
182
+ if (!hasParentUpdate) {
183
+ delete patch.depth;
184
+ return {
185
+ ...input,
186
+ patch,
187
+ };
188
+ }
189
+
190
+ const currentLinkId = normalizeNonEmpty(input.id);
191
+ if (!currentLinkId) {
192
+ throw new Error('Document structure link id is required.');
193
+ }
194
+
195
+ if (typeof loadDocumentLinkById !== 'function') {
196
+ throw new Error('docman_parent_lookup_unavailable');
197
+ }
198
+
199
+ const currentLink = await loadDocumentLinkById(currentLinkId);
200
+ if (!currentLink?.id) {
201
+ throw new Error('Document structure link could not be resolved.');
202
+ }
203
+
204
+ const nextKind = inferDocumentLinkKind({ ...currentLink, ...patch });
205
+ const parentLinkId = normalizeNonEmpty(patch.parentLinkId);
206
+
207
+ let parentLink = null;
208
+ if (parentLinkId) {
209
+ parentLink = await loadDocumentLinkById(parentLinkId);
210
+ if (!parentLink?.id) {
211
+ throw new Error('Selected parent section could not be resolved.');
212
+ }
213
+ if (inferDocumentLinkKind(parentLink) !== 'section') {
214
+ throw new Error(
215
+ nextKind === 'page'
216
+ ? 'Pages cannot be nested under another page.'
217
+ : 'Sections can only be nested under other sections.'
218
+ );
219
+ }
220
+ }
221
+
222
+ patch.parentLinkId = parentLinkId || null;
223
+ patch.depth = parentLinkId ? toDepth(parentLink?.depth) + 1 : 0;
224
+
225
+ return {
226
+ ...input,
227
+ patch,
228
+ };
229
+ }
230
+
231
+ function sanitizeSectionRecord(record) {
232
+ return stripKeys(record, SECTION_CONTENT_FIELDS);
233
+ }
234
+
235
+ function sanitizeSectionPageLinkRecord(record) {
236
+ const item = { ...toRecord(record) };
237
+ const sectionId = normalizeNonEmpty(item.sectionId);
238
+ if (sectionId) {
239
+ item.sectionId = sectionId;
240
+ }
241
+ delete item.parentLinkId;
242
+ item.depth = 0;
243
+ return item;
244
+ }
245
+
246
+ function sanitizeOutputShape(output, sanitizer) {
247
+ if (Array.isArray(output)) {
248
+ return output.map((item) => sanitizer(item));
249
+ }
250
+
251
+ const record = toRecord(output);
252
+ if (Array.isArray(record.items)) {
253
+ return {
254
+ ...record,
255
+ items: record.items.map((item) => sanitizer(item)),
256
+ };
257
+ }
258
+ if (record.item && typeof record.item === 'object') {
259
+ return {
260
+ ...record,
261
+ item: sanitizer(record.item),
262
+ };
263
+ }
264
+ if (Object.keys(record).length > 0) {
265
+ return sanitizer(record);
266
+ }
267
+ return output;
268
+ }
269
+
270
+ function normalizePageParentsToSections(items = []) {
271
+ const rows = Array.isArray(items) ? items.map((item) => ({ ...toRecord(item) })) : [];
272
+ const byId = new Map();
273
+ rows.forEach((row) => {
274
+ const id = getDocumentLinkIdentity(row);
275
+ if (id) byId.set(id, row);
276
+ });
277
+
278
+ const resolveSectionAncestor = (row) => {
279
+ let parentLinkId = normalizeNonEmpty(row.parentLinkId);
280
+ while (parentLinkId) {
281
+ const parent = byId.get(parentLinkId);
282
+ if (!parent) return null;
283
+ if (inferDocumentLinkKind(parent) === 'section') {
284
+ return parent;
285
+ }
286
+ parentLinkId = normalizeNonEmpty(parent.parentLinkId);
287
+ }
288
+ return null;
289
+ };
290
+
291
+ return rows.map((row) => {
292
+ if (inferDocumentLinkKind(row) !== 'page') return row;
293
+ const sectionParent = resolveSectionAncestor(row);
294
+ if (!sectionParent) {
295
+ delete row.parentLinkId;
296
+ row.depth = 0;
297
+ return row;
298
+ }
299
+ row.parentLinkId = getDocumentLinkIdentity(sectionParent) || undefined;
300
+ row.depth = toDepth(sectionParent.depth) + 1;
301
+ return row;
302
+ });
303
+ }
304
+
305
+ function sanitizeDocumentComposeIndex(output) {
306
+ const record = toRecord(output);
307
+ if (!Array.isArray(record.items)) return output;
308
+ return {
309
+ ...record,
310
+ items: normalizePageParentsToSections(record.items),
311
+ };
312
+ }
313
+
314
+ export function getAllowedDocmanOperationIds() {
315
+ return [...DOCMAN_ALLOWED_OPERATION_IDS];
316
+ }
317
+
318
+ export function isDocmanOperationAllowed(operationId) {
319
+ const normalized = normalizeNonEmpty(operationId).toLowerCase();
320
+ if (!normalized) return false;
321
+ return DOCMAN_ALLOWED_OPERATION_IDS.includes(normalized);
322
+ }
323
+
324
+ function resolveDocmanResourceId(operation) {
325
+ const record = toRecord(operation);
326
+ const tags = Array.isArray(record.tags) ? record.tags : [];
327
+ const resourceTag = tags.find((tag) => normalizeNonEmpty(tag).toLowerCase().startsWith('resource:'));
328
+ if (resourceTag) {
329
+ return normalizeNonEmpty(resourceTag.slice('resource:'.length)).toLowerCase();
330
+ }
331
+ const operationId = normalizeNonEmpty(record.operationId).toLowerCase();
332
+ const [prefix, second] = operationId.split('.');
333
+ return normalizeNonEmpty(second || prefix).toLowerCase();
334
+ }
335
+
336
+ export function filterDocmanManifest(manifest) {
337
+ const record = toRecord(manifest);
338
+ const capabilities = toRecord(record.capabilities);
339
+ const operations = Array.isArray(capabilities.operations)
340
+ ? capabilities.operations.filter((operation) => isDocmanOperationAllowed(operation?.operationId))
341
+ : [];
342
+ const allowedResourceIds = new Set(
343
+ operations.map((operation) => resolveDocmanResourceId(operation)).filter(Boolean)
344
+ );
345
+ const resources = Array.isArray(capabilities.resources)
346
+ ? capabilities.resources.filter((resource) => allowedResourceIds.has(normalizeNonEmpty(resource?.resourceId).toLowerCase()))
347
+ : [];
348
+ const docs = toRecord(record.docs);
349
+ const operationsDocMap = toRecord(docs.operations);
350
+ const resourcesDocMap = toRecord(docs.resources);
351
+ const filteredDocEntries = Object.fromEntries(
352
+ Object.entries(operationsDocMap).filter(([operationId]) => isDocmanOperationAllowed(operationId))
353
+ );
354
+ const filteredResourceEntries = Object.fromEntries(
355
+ Object.entries(resourcesDocMap).filter(([resourceId]) => allowedResourceIds.has(normalizeNonEmpty(resourceId).toLowerCase()))
356
+ );
357
+
358
+ return {
359
+ ...record,
360
+ capabilities: {
361
+ ...capabilities,
362
+ operations,
363
+ ...(resources.length > 0 ? { resources } : {}),
364
+ },
365
+ docs: {
366
+ ...docs,
367
+ ...(Object.keys(filteredResourceEntries).length > 0 ? { resources: filteredResourceEntries } : {}),
368
+ operations: filteredDocEntries,
369
+ },
370
+ };
371
+ }
372
+
373
+ export function filterDocmanHostRouteProjection(routeProjection) {
374
+ const routes = toAllowedRouteList(routeProjection);
375
+ if (Array.isArray(routeProjection)) return routes;
376
+ return {
377
+ ...toRecord(routeProjection),
378
+ routes,
379
+ };
380
+ }
381
+
382
+ export async function sanitizeDocmanOperationInput(
383
+ operationId,
384
+ input,
385
+ { loadDocumentLinkById } = {},
386
+ ) {
387
+ const normalizedOperationId = normalizeNonEmpty(operationId).toLowerCase();
388
+
389
+ if (normalizedOperationId === 'section.create') {
390
+ return sanitizeSectionPayload(input, 'data');
391
+ }
392
+ if (normalizedOperationId === 'section.update') {
393
+ return sanitizeSectionPayload(input, 'patch');
394
+ }
395
+ if (normalizedOperationId === 'document-section-link.create') {
396
+ return sanitizeDocumentSectionLinkCreateInput(input, loadDocumentLinkById);
397
+ }
398
+ if (normalizedOperationId === 'document-section-link.update') {
399
+ return sanitizeDocumentSectionLinkUpdateInput(input, loadDocumentLinkById);
400
+ }
401
+ if (normalizedOperationId === 'section-page-link.create') {
402
+ return sanitizeSectionPageLinkPayload(input, 'data');
403
+ }
404
+ if (normalizedOperationId === 'section-page-link.update') {
405
+ return sanitizeSectionPageLinkPayload(input, 'patch');
406
+ }
407
+
408
+ return input;
409
+ }
410
+
411
+ export function sanitizeDocmanOperationOutput(operationId, output) {
412
+ const normalizedOperationId = normalizeNonEmpty(operationId).toLowerCase();
413
+
414
+ if (
415
+ normalizedOperationId === 'section.list' ||
416
+ normalizedOperationId === 'section.get' ||
417
+ normalizedOperationId === 'section.create' ||
418
+ normalizedOperationId === 'section.update'
419
+ ) {
420
+ return sanitizeOutputShape(output, sanitizeSectionRecord);
421
+ }
422
+
423
+ if (normalizedOperationId === 'document-section-link.list') {
424
+ const sanitized = sanitizeOutputShape(output, (item) => item);
425
+ if (Array.isArray(sanitized)) {
426
+ return normalizePageParentsToSections(sanitized);
427
+ }
428
+ if (Array.isArray(sanitized?.items)) {
429
+ return {
430
+ ...sanitized,
431
+ items: normalizePageParentsToSections(sanitized.items),
432
+ };
433
+ }
434
+ return sanitized;
435
+ }
436
+
437
+ if (normalizedOperationId === 'document.compose.index') {
438
+ return sanitizeDocumentComposeIndex(output);
439
+ }
440
+
441
+ if (
442
+ normalizedOperationId === 'section-page-link.list' ||
443
+ normalizedOperationId === 'section-page-link.create' ||
444
+ normalizedOperationId === 'section-page-link.update'
445
+ ) {
446
+ return sanitizeOutputShape(output, sanitizeSectionPageLinkRecord);
447
+ }
448
+
449
+ return output;
450
+ }
@@ -0,0 +1,20 @@
1
+ import {
2
+ buildDocmanDomainCapabilityManifest as buildBaseDocmanDomainCapabilityManifest,
3
+ buildDocmanHostRouteProjection as buildBaseDocmanHostRouteProjection,
4
+ } from '@aopslab/domain-kit-docman/operations';
5
+
6
+ import {
7
+ filterDocmanHostRouteProjection,
8
+ filterDocmanManifest,
9
+ getAllowedDocmanOperationIds,
10
+ } from './docman-policy.mjs';
11
+
12
+ export function buildAopsDocmanDomainCapabilityManifest(options = {}) {
13
+ return filterDocmanManifest(buildBaseDocmanDomainCapabilityManifest(options));
14
+ }
15
+
16
+ export function buildAopsDocmanHostRouteProjection(options = {}) {
17
+ return filterDocmanHostRouteProjection(buildBaseDocmanHostRouteProjection(options));
18
+ }
19
+
20
+ export { getAllowedDocmanOperationIds };