@aopslab/domain-kit-docman 0.1.4

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 (64) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +79 -0
  4. package/dist/config/config.d.ts +55 -0
  5. package/dist/config/config.js +176 -0
  6. package/dist/domain-services/index.d.ts +7 -0
  7. package/dist/domain-services/index.js +7 -0
  8. package/dist/domain-services/jwt.d.ts +1 -0
  9. package/dist/domain-services/jwt.js +5 -0
  10. package/dist/domain-services/metrics.d.ts +5 -0
  11. package/dist/domain-services/metrics.js +7 -0
  12. package/dist/domain-services/presets.d.ts +3 -0
  13. package/dist/domain-services/presets.js +4 -0
  14. package/dist/domain-services/provider.d.ts +2 -0
  15. package/dist/domain-services/provider.js +564 -0
  16. package/dist/domain-services/resilience.d.ts +6 -0
  17. package/dist/domain-services/resilience.js +19 -0
  18. package/dist/domain-services/types.d.ts +179 -0
  19. package/dist/domain-services/types.js +1 -0
  20. package/dist/domain-services/unified.d.ts +272 -0
  21. package/dist/domain-services/unified.js +210 -0
  22. package/dist/errors/error.map.d.ts +21 -0
  23. package/dist/errors/error.map.js +47 -0
  24. package/dist/errors/friendly.d.ts +31 -0
  25. package/dist/errors/friendly.js +407 -0
  26. package/dist/errors/i18n.d.ts +4 -0
  27. package/dist/errors/i18n.js +10 -0
  28. package/dist/errors/index.d.ts +3 -0
  29. package/dist/errors/index.js +3 -0
  30. package/dist/index.d.ts +9 -0
  31. package/dist/index.js +7 -0
  32. package/dist/operations/catalog.d.ts +11 -0
  33. package/dist/operations/catalog.js +299 -0
  34. package/dist/operations/contract.d.ts +31 -0
  35. package/dist/operations/contract.js +667 -0
  36. package/dist/operations/dcm.d.ts +58 -0
  37. package/dist/operations/dcm.js +168 -0
  38. package/dist/operations/definition.d.ts +6 -0
  39. package/dist/operations/definition.js +140 -0
  40. package/dist/operations/executor.d.ts +10 -0
  41. package/dist/operations/executor.js +269 -0
  42. package/dist/operations/host-projection.d.ts +11 -0
  43. package/dist/operations/host-projection.js +156 -0
  44. package/dist/operations/index.d.ts +10 -0
  45. package/dist/operations/index.js +10 -0
  46. package/dist/operations/io-types.d.ts +125 -0
  47. package/dist/operations/io-types.js +1 -0
  48. package/dist/operations/schemas.d.ts +10 -0
  49. package/dist/operations/schemas.js +872 -0
  50. package/dist/operations/scope-owned-create.d.ts +3 -0
  51. package/dist/operations/scope-owned-create.js +12 -0
  52. package/dist/operations/tool-input.d.ts +11 -0
  53. package/dist/operations/tool-input.js +357 -0
  54. package/dist/operations/types.d.ts +37 -0
  55. package/dist/operations/types.js +1 -0
  56. package/dist/resources/index.d.ts +1 -0
  57. package/dist/resources/index.js +1 -0
  58. package/dist/resources/resources.docman.server.errors.d.ts +14 -0
  59. package/dist/resources/resources.docman.server.errors.js +36 -0
  60. package/dist/shared/index.d.ts +5 -0
  61. package/dist/shared/index.js +1 -0
  62. package/dist/shared/tool-input-guard.d.ts +1 -0
  63. package/dist/shared/tool-input-guard.js +1 -0
  64. package/package.json +69 -0
@@ -0,0 +1,58 @@
1
+ import type { DocmanOperationSideEffect } from './contract.js';
2
+ import type { DocmanOperationPolicy } from './types.js';
3
+ export type DocmanDomainCapabilityOperation = {
4
+ operationId: string;
5
+ title?: string;
6
+ sideEffect?: DocmanOperationSideEffect;
7
+ tags?: string[];
8
+ inputSchemaRef?: string;
9
+ outputSchemaRef?: string;
10
+ };
11
+ export type DocmanDomainCapabilityOperationDocs = {
12
+ summary?: string;
13
+ notes?: string[];
14
+ examples?: string[];
15
+ antiPatterns?: string[];
16
+ preconditions?: string[];
17
+ postconditions?: string[];
18
+ };
19
+ export type DocmanDomainCapabilityResource = {
20
+ resourceId: string;
21
+ title: string;
22
+ kind?: string;
23
+ };
24
+ export type DocmanDomainDiscoveryDocs = {
25
+ summary?: string;
26
+ notes?: string[];
27
+ };
28
+ export type DocmanDomainCapabilityManifest = {
29
+ manifestVersion: string;
30
+ domain: {
31
+ id: string;
32
+ version: string;
33
+ displayName?: string;
34
+ description?: string;
35
+ };
36
+ capabilities: {
37
+ operations: DocmanDomainCapabilityOperation[];
38
+ resources?: DocmanDomainCapabilityResource[];
39
+ };
40
+ contracts?: {
41
+ schemas: Record<string, unknown>;
42
+ };
43
+ policies?: {
44
+ operations: Record<string, DocmanOperationPolicy>;
45
+ };
46
+ docs?: {
47
+ domain?: DocmanDomainDiscoveryDocs;
48
+ resources?: Record<string, DocmanDomainDiscoveryDocs>;
49
+ operations?: Record<string, DocmanDomainCapabilityOperationDocs>;
50
+ };
51
+ };
52
+ export type BuildDocmanDomainCapabilityManifestOptions = {
53
+ manifestVersion?: string;
54
+ domainVersion?: string;
55
+ includeDocs?: boolean;
56
+ refresh?: boolean;
57
+ };
58
+ export declare function buildDocmanDomainCapabilityManifest(options?: BuildDocmanDomainCapabilityManifestOptions): DocmanDomainCapabilityManifest;
@@ -0,0 +1,168 @@
1
+ import { listDocmanOperationContracts } from './contract.js';
2
+ import { getDocmanContractSchema, resolveDocmanSchemaRefName } from './schemas.js';
3
+ function toTags(operation) {
4
+ const tags = new Set();
5
+ for (const tag of operation.tags ?? []) {
6
+ const normalized = String(tag ?? '').trim();
7
+ if (!normalized)
8
+ continue;
9
+ tags.add(normalized);
10
+ }
11
+ tags.add(`kind:${operation.kind}`);
12
+ tags.add(`resource:${operation.serviceEntity}`);
13
+ tags.add(`service:${operation.serviceKey}`);
14
+ return [...tags];
15
+ }
16
+ function humanizeResource(resource) {
17
+ const label = resource
18
+ .replace(/^docman\./, '')
19
+ .replace(/[._-]+/g, ' ')
20
+ .replace(/\s+/g, ' ')
21
+ .trim();
22
+ if (!label)
23
+ return 'Resource';
24
+ return label.charAt(0).toUpperCase() + label.slice(1);
25
+ }
26
+ function inferResourceKind(resourceId) {
27
+ if (resourceId.includes('link'))
28
+ return 'relationship';
29
+ if (resourceId.includes('version'))
30
+ return 'versioned-record';
31
+ if (resourceId.includes('asset') || resourceId.includes('snippet') || resourceId.includes('embed'))
32
+ return 'content-asset';
33
+ return 'document-record';
34
+ }
35
+ function buildResourceSummary(resourceTitle, operationKinds) {
36
+ if (operationKinds.length === 0) {
37
+ return `${resourceTitle} records used to compose, version, and render documentation content.`;
38
+ }
39
+ if (operationKinds.length === 1) {
40
+ return `Supports ${operationKinds[0]} operations for ${resourceTitle.toLowerCase()} in documentation workflows.`;
41
+ }
42
+ return `Supports ${operationKinds.slice(0, 5).join(', ')} operations for ${resourceTitle.toLowerCase()} in documentation workflows.`;
43
+ }
44
+ function buildCapabilityResources(operations) {
45
+ const seen = new Map();
46
+ for (const operation of operations) {
47
+ const resourceId = String(operation.serviceEntity ?? '').trim();
48
+ if (!resourceId || seen.has(resourceId))
49
+ continue;
50
+ seen.set(resourceId, {
51
+ resourceId,
52
+ title: humanizeResource(resourceId),
53
+ kind: inferResourceKind(resourceId),
54
+ });
55
+ }
56
+ return [...seen.values()].sort((left, right) => left.title.localeCompare(right.title));
57
+ }
58
+ function buildResourceDocs(operations) {
59
+ const operationKindsByResource = new Map();
60
+ for (const operation of operations) {
61
+ const resourceId = String(operation.serviceEntity ?? '').trim();
62
+ if (!resourceId)
63
+ continue;
64
+ const existing = operationKindsByResource.get(resourceId) ?? new Set();
65
+ existing.add(operation.kind);
66
+ operationKindsByResource.set(resourceId, existing);
67
+ }
68
+ return Object.fromEntries([...operationKindsByResource.entries()]
69
+ .sort(([left], [right]) => humanizeResource(left).localeCompare(humanizeResource(right)))
70
+ .map(([resourceId, operationKinds]) => [
71
+ resourceId,
72
+ {
73
+ summary: buildResourceSummary(humanizeResource(resourceId), [...operationKinds]),
74
+ },
75
+ ]));
76
+ }
77
+ function toOperationDocs(operation) {
78
+ const requiredArgs = operation.args.filter((arg) => !arg.optional).map((arg) => arg.name);
79
+ const optionalArgs = operation.args.filter((arg) => arg.optional).map((arg) => arg.name);
80
+ const notes = [...(operation.notes ?? [])];
81
+ if (requiredArgs.length > 0)
82
+ notes.push(`required args: ${requiredArgs.join(', ')}`);
83
+ if (optionalArgs.length > 0)
84
+ notes.push(`optional args: ${optionalArgs.join(', ')}`);
85
+ return {
86
+ summary: operation.summary,
87
+ ...(notes.length > 0 ? { notes } : {}),
88
+ ...(operation.examples && operation.examples.length > 0 ? { examples: [...operation.examples] } : {}),
89
+ ...(operation.antiPatterns && operation.antiPatterns.length > 0 ? { antiPatterns: [...operation.antiPatterns] } : {}),
90
+ ...(operation.preconditions && operation.preconditions.length > 0 ? { preconditions: [...operation.preconditions] } : {}),
91
+ ...(operation.postconditions && operation.postconditions.length > 0 ? { postconditions: [...operation.postconditions] } : {}),
92
+ };
93
+ }
94
+ function toCapabilityOperation(operation) {
95
+ const inputSchemaRef = resolveDocmanSchemaRefName(operation.inputSchema);
96
+ const outputSchemaRef = resolveDocmanSchemaRefName(operation.outputSchema);
97
+ return {
98
+ operationId: operation.operationId,
99
+ title: operation.summary,
100
+ sideEffect: operation.sideEffect,
101
+ tags: toTags(operation),
102
+ ...(inputSchemaRef ? { inputSchemaRef } : {}),
103
+ ...(outputSchemaRef ? { outputSchemaRef } : {}),
104
+ };
105
+ }
106
+ export function buildDocmanDomainCapabilityManifest(options = {}) {
107
+ const operations = listDocmanOperationContracts({ refresh: options.refresh });
108
+ const manifest = {
109
+ manifestVersion: options.manifestVersion ?? '1.0.0',
110
+ domain: {
111
+ id: 'docman',
112
+ version: options.domainVersion ?? '0.0.0',
113
+ displayName: 'Docman',
114
+ description: 'Document composition and content-structure tooling for documents, sections, pages, snippets, assets, embeds, and version history.',
115
+ },
116
+ capabilities: {
117
+ operations: operations.map(toCapabilityOperation),
118
+ resources: buildCapabilityResources(operations),
119
+ },
120
+ };
121
+ if (options.includeDocs !== false) {
122
+ manifest.docs = {
123
+ domain: {
124
+ summary: 'Create, organize, version, render, and link documentation content across documents, sections, pages, snippets, assets, embeds, and structure links.',
125
+ notes: [
126
+ 'page-version.format accepts md and mdx as native source formats',
127
+ 'native compose/fetch accepts md and mdx source today',
128
+ 'publish materialize currently returns deterministic markdown or html text payloads',
129
+ 'publish target dispatch is registry-backed internally; future artifact exporters remain a separate contract track',
130
+ 'asset:// logical references in md/mdx source resolve through asset and asset-version ownership during compose',
131
+ 'asset and asset-version now provide the publish-grade ownership foundation for images and attached resources',
132
+ 'embed records remain the lightweight placement/render-hint layer that can later bind to asset ownership',
133
+ ],
134
+ },
135
+ resources: buildResourceDocs(operations),
136
+ operations: Object.fromEntries(operations.map((operation) => [operation.operationId, toOperationDocs(operation)])),
137
+ };
138
+ }
139
+ const operationPolicies = {};
140
+ for (const operation of operations) {
141
+ if (!operation.policy)
142
+ continue;
143
+ operationPolicies[operation.operationId] = operation.policy;
144
+ }
145
+ if (Object.keys(operationPolicies).length > 0) {
146
+ manifest.policies = { operations: operationPolicies };
147
+ }
148
+ const schemaRefs = new Set();
149
+ for (const operation of manifest.capabilities.operations) {
150
+ if (operation.inputSchemaRef)
151
+ schemaRefs.add(operation.inputSchemaRef);
152
+ if (operation.outputSchemaRef)
153
+ schemaRefs.add(operation.outputSchemaRef);
154
+ }
155
+ if (schemaRefs.size > 0) {
156
+ const schemas = {};
157
+ for (const ref of schemaRefs) {
158
+ const schema = getDocmanContractSchema(ref);
159
+ if (!schema)
160
+ continue;
161
+ schemas[ref] = schema;
162
+ }
163
+ if (Object.keys(schemas).length > 0) {
164
+ manifest.contracts = { schemas };
165
+ }
166
+ }
167
+ return manifest;
168
+ }
@@ -0,0 +1,6 @@
1
+ import type { DefineDocmanKitOperationInput, DocmanOperationSpec } from './types.js';
2
+ export declare function normalizeDocmanOperationId(value: string): string;
3
+ export declare function buildDocmanToolIdFromOperation(operationId: string): string;
4
+ export declare function defineDocmanKitOperation(input: DefineDocmanKitOperationInput): DocmanOperationSpec;
5
+ export declare function defineDocmanKitOperations(input: DefineDocmanKitOperationInput[]): DocmanOperationSpec[];
6
+ export declare function cloneDocmanOperationSpec(spec: DocmanOperationSpec): DocmanOperationSpec;
@@ -0,0 +1,140 @@
1
+ const TOOL_PREFIX = 'docman-';
2
+ function normalizeNonEmpty(value) {
3
+ return String(value ?? '').trim();
4
+ }
5
+ function normalizeTags(tags) {
6
+ if (!tags || tags.length === 0)
7
+ return undefined;
8
+ const unique = new Set();
9
+ for (const tag of tags) {
10
+ const normalized = String(tag ?? '').trim();
11
+ if (!normalized)
12
+ continue;
13
+ unique.add(normalized);
14
+ }
15
+ if (unique.size === 0)
16
+ return undefined;
17
+ return [...unique];
18
+ }
19
+ function cloneArgs(args) {
20
+ if (!args || args.length === 0)
21
+ return [];
22
+ return args.map((arg) => ({
23
+ name: String(arg.name ?? '').trim(),
24
+ optional: arg.optional === true,
25
+ }));
26
+ }
27
+ function normalizeExamples(examples) {
28
+ if (!examples || examples.length === 0)
29
+ return undefined;
30
+ const normalized = examples.map((example) => String(example ?? '').trim()).filter(Boolean);
31
+ if (normalized.length === 0)
32
+ return undefined;
33
+ return normalized;
34
+ }
35
+ function normalizeStringList(values) {
36
+ if (!values || values.length === 0)
37
+ return undefined;
38
+ const unique = new Set();
39
+ for (const value of values) {
40
+ const normalized = String(value ?? '').trim();
41
+ if (!normalized)
42
+ continue;
43
+ unique.add(normalized);
44
+ }
45
+ if (unique.size === 0)
46
+ return undefined;
47
+ return [...unique];
48
+ }
49
+ function normalizeDocs(docs) {
50
+ if (!docs)
51
+ return undefined;
52
+ const notes = normalizeStringList(docs.notes);
53
+ const antiPatterns = normalizeStringList(docs.antiPatterns);
54
+ const preconditions = normalizeStringList(docs.preconditions);
55
+ const postconditions = normalizeStringList(docs.postconditions);
56
+ const normalized = {
57
+ ...(notes ? { notes } : {}),
58
+ ...(antiPatterns ? { antiPatterns } : {}),
59
+ ...(preconditions ? { preconditions } : {}),
60
+ ...(postconditions ? { postconditions } : {}),
61
+ };
62
+ return Object.keys(normalized).length > 0 ? normalized : undefined;
63
+ }
64
+ export function normalizeDocmanOperationId(value) {
65
+ return value
66
+ .trim()
67
+ .replace(/\s+/g, '-')
68
+ .replace(/[^a-zA-Z0-9.-]/g, '-')
69
+ .replace(/-+/g, '-')
70
+ .replace(/\.+/g, '.')
71
+ .replace(/^-+/, '')
72
+ .replace(/-+$/, '')
73
+ .toLowerCase();
74
+ }
75
+ export function buildDocmanToolIdFromOperation(operationId) {
76
+ return `${TOOL_PREFIX}${normalizeDocmanOperationId(operationId).replace(/\./g, '-')}`;
77
+ }
78
+ function normalizeDocmanToolId(toolId) {
79
+ const normalized = String(toolId ?? '').trim().toLowerCase();
80
+ if (!normalized)
81
+ return normalized;
82
+ return normalized
83
+ .replace(/\s+/g, '-')
84
+ .replace(/[^a-z0-9-]/g, '-')
85
+ .replace(/-+/g, '-')
86
+ .replace(/^-+/, '')
87
+ .replace(/-+$/, '');
88
+ }
89
+ export function defineDocmanKitOperation(input) {
90
+ const operationId = normalizeDocmanOperationId(input.operationId);
91
+ if (!operationId)
92
+ throw new Error('invalid_docman_operation_id');
93
+ const serviceKey = normalizeNonEmpty(input.serviceKey);
94
+ if (!serviceKey)
95
+ throw new Error(`invalid_docman_operation_service_key:${operationId}`);
96
+ const serviceEntity = normalizeNonEmpty(input.serviceEntity);
97
+ if (!serviceEntity)
98
+ throw new Error(`invalid_docman_operation_service_entity:${operationId}`);
99
+ const methodName = normalizeNonEmpty(input.methodName);
100
+ if (!methodName)
101
+ throw new Error(`invalid_docman_operation_method_name:${operationId}`);
102
+ const toolIdSource = input.toolId ?? buildDocmanToolIdFromOperation(operationId);
103
+ const toolId = normalizeDocmanToolId(toolIdSource);
104
+ if (!toolId)
105
+ throw new Error(`invalid_docman_operation_tool_id:${operationId}`);
106
+ const summary = normalizeNonEmpty(input.summary ?? '');
107
+ const tags = normalizeTags(input.tags);
108
+ const examples = normalizeExamples(input.examples);
109
+ const docs = normalizeDocs(input.docs);
110
+ return {
111
+ operationId,
112
+ toolId,
113
+ serviceKey,
114
+ serviceEntity,
115
+ methodName,
116
+ kind: input.kind,
117
+ args: cloneArgs(input.args),
118
+ ...(summary ? { summary } : {}),
119
+ ...(tags ? { tags } : {}),
120
+ ...(input.sideEffect ? { sideEffect: input.sideEffect } : {}),
121
+ ...(input.inputSchema !== undefined ? { inputSchema: input.inputSchema } : {}),
122
+ ...(input.outputSchema !== undefined ? { outputSchema: input.outputSchema } : {}),
123
+ ...(input.policy !== undefined ? { policy: input.policy } : {}),
124
+ ...(examples ? { examples } : {}),
125
+ ...(docs ? { docs } : {}),
126
+ };
127
+ }
128
+ export function defineDocmanKitOperations(input) {
129
+ return input.map(defineDocmanKitOperation);
130
+ }
131
+ export function cloneDocmanOperationSpec(spec) {
132
+ const docs = normalizeDocs(spec.docs);
133
+ return {
134
+ ...spec,
135
+ args: cloneArgs(spec.args),
136
+ ...(spec.tags ? { tags: [...spec.tags] } : {}),
137
+ ...(spec.examples ? { examples: [...spec.examples] } : {}),
138
+ ...(docs ? { docs } : {}),
139
+ };
140
+ }
@@ -0,0 +1,10 @@
1
+ import type { DocmanOperationInput, DocmanOperationOutput, DocmanTypedOperationId } from './io-types.js';
2
+ export declare function runDocmanKitOperationByToolId(toolId: string, input: unknown): Promise<unknown>;
3
+ export declare function runDocmanKitOperationById<TId extends DocmanTypedOperationId>(operationId: TId, input: DocmanOperationInput<TId>): Promise<DocmanOperationOutput<TId>>;
4
+ export declare function runDocmanKitOperationByTypedId<TId extends DocmanTypedOperationId>(operationId: TId, input: DocmanOperationInput<TId>): Promise<DocmanOperationOutput<TId>>;
5
+ export declare function runDocmanKitOperation(input: unknown, identifier: {
6
+ toolId: string;
7
+ } | {
8
+ operationId: string;
9
+ }): Promise<unknown>;
10
+ export declare function clearDocmanKitOperationCaches(): void;
@@ -0,0 +1,269 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { Effect } from 'effect';
4
+ import { config as loadDotEnv } from 'dotenv';
5
+ import { createDocmanKitWithEnv } from '../domain-services/unified.js';
6
+ import { getDocmanKitEnvConfig } from '../config/config.js';
7
+ import { getDocmanOperationContractById, getDocmanOperationContractByToolId } from './contract.js';
8
+ import { isDocmanScopeOwnedCreateOperation } from './scope-owned-create.js';
9
+ const HOST_META_KEYS = new Set([
10
+ 'scopeId',
11
+ 'scopeResolution',
12
+ 'tenantId',
13
+ 'locale',
14
+ 'fallbackLocale',
15
+ '__hostContext',
16
+ ]);
17
+ const DOCMAN_OBJECT_INPUT_CUSTOM_OPERATION_IDS = new Set([
18
+ 'document.index.build',
19
+ 'document.index.get',
20
+ 'document.summary.build',
21
+ 'document.summary.get',
22
+ 'document.search',
23
+ 'document.scope.search',
24
+ 'document.answer-pack',
25
+ 'document.compose.fetch',
26
+ 'document.publish.materialize',
27
+ 'document-version.import-headings',
28
+ 'document-version.set-current',
29
+ ]);
30
+ let envLoaded = false;
31
+ let cachedServices = null;
32
+ function toRecord(input) {
33
+ if (!input || typeof input !== 'object' || Array.isArray(input))
34
+ return {};
35
+ return input;
36
+ }
37
+ function toSnakeCase(value) {
38
+ return value
39
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
40
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1_$2')
41
+ .replace(/[-\s]+/g, '_')
42
+ .toLowerCase();
43
+ }
44
+ function normalizeNonEmpty(value) {
45
+ if (typeof value !== 'string')
46
+ return undefined;
47
+ const trimmed = value.trim();
48
+ return trimmed.length > 0 ? trimmed : undefined;
49
+ }
50
+ function resolveScopeIdValue(payload) {
51
+ return normalizeNonEmpty(payload.scopeId);
52
+ }
53
+ function normalizeScopeResolution(value) {
54
+ const normalized = normalizeNonEmpty(value);
55
+ if (normalized === 'explicit' || normalized === 'cascade')
56
+ return normalized;
57
+ return undefined;
58
+ }
59
+ function toObjectOrUndefined(value) {
60
+ if (!value || typeof value !== 'object' || Array.isArray(value))
61
+ return undefined;
62
+ return value;
63
+ }
64
+ function sanitizePayload(payload, operation) {
65
+ const allowScopeId = operation?.args.some((arg) => arg.name === 'scopeId') === true;
66
+ const allowScopeResolution = operation?.args.some((arg) => arg.name === 'scopeResolution') === true;
67
+ const sanitized = {};
68
+ for (const [key, value] of Object.entries(payload)) {
69
+ if (key === 'scopeId' && allowScopeId) {
70
+ sanitized[key] = value;
71
+ continue;
72
+ }
73
+ if (key === 'scopeResolution' && allowScopeResolution) {
74
+ sanitized[key] = value;
75
+ continue;
76
+ }
77
+ if (HOST_META_KEYS.has(key))
78
+ continue;
79
+ sanitized[key] = value;
80
+ }
81
+ return sanitized;
82
+ }
83
+ function buildLocaleOptions(payload) {
84
+ const locale = normalizeNonEmpty(payload.locale);
85
+ const fallbackLocale = normalizeNonEmpty(payload.fallbackLocale);
86
+ if (!locale && !fallbackLocale)
87
+ return undefined;
88
+ return {
89
+ ...(locale ? { locale } : {}),
90
+ ...(fallbackLocale ? { fallbackLocale } : {}),
91
+ };
92
+ }
93
+ function loadEnvOnce() {
94
+ if (envLoaded)
95
+ return;
96
+ envLoaded = true;
97
+ const candidates = [
98
+ process.env.DOTENV_CONFIG_PATH,
99
+ process.env.DOCMAN_ENV_PATH,
100
+ path.resolve(process.cwd(), '.env'),
101
+ path.resolve(process.cwd(), '..', '.env'),
102
+ path.resolve(process.cwd(), '../..', '.env'),
103
+ ].filter(Boolean);
104
+ for (const candidate of candidates) {
105
+ if (!candidate)
106
+ continue;
107
+ if (!fs.existsSync(candidate))
108
+ continue;
109
+ loadDotEnv({ path: candidate, quiet: true });
110
+ break;
111
+ }
112
+ }
113
+ async function getServices() {
114
+ if (cachedServices)
115
+ return cachedServices;
116
+ cachedServices = (async () => {
117
+ loadEnvOnce();
118
+ const envConfig = getDocmanKitEnvConfig();
119
+ const { kit } = createDocmanKitWithEnv({
120
+ envConfig,
121
+ baseContext: { tenantId: envConfig.tenantId },
122
+ });
123
+ const services = await kit.createAll();
124
+ return Object.fromEntries(Object.entries(services));
125
+ })();
126
+ return cachedServices;
127
+ }
128
+ function resolveCustomArgValue(payload, arg) {
129
+ if (arg.name === 'options') {
130
+ const explicitOptions = toObjectOrUndefined(payload.options);
131
+ if (explicitOptions)
132
+ return explicitOptions;
133
+ return buildLocaleOptions(payload);
134
+ }
135
+ if (arg.name === 'scopeId') {
136
+ return resolveScopeIdValue(payload);
137
+ }
138
+ if (arg.name === 'scopeResolution') {
139
+ return normalizeScopeResolution(payload.scopeResolution);
140
+ }
141
+ return payload[arg.name];
142
+ }
143
+ function buildCrudArgs(operation, payload) {
144
+ const id = normalizeNonEmpty(payload.id);
145
+ if (operation.kind === 'list') {
146
+ const optionSeed = { ...(toObjectOrUndefined(payload.options) ?? {}) };
147
+ if (typeof payload.includeVersionInfo === 'boolean') {
148
+ optionSeed.includeVersionInfo = payload.includeVersionInfo;
149
+ }
150
+ const options = Object.keys(optionSeed).length > 0 ? optionSeed : undefined;
151
+ const filter = { ...(toObjectOrUndefined(payload.filter) ?? {}) };
152
+ const scopeId = resolveScopeIdValue(payload);
153
+ if (scopeId && filter.scopeId === undefined) {
154
+ filter.scopeId = scopeId;
155
+ }
156
+ const scopeResolution = normalizeScopeResolution(payload.scopeResolution);
157
+ if (scopeResolution && filter.scopeResolution === undefined) {
158
+ filter.scopeResolution = scopeResolution;
159
+ }
160
+ return [filter, options];
161
+ }
162
+ if (operation.kind === 'get') {
163
+ const options = toObjectOrUndefined(payload.options);
164
+ if (!id)
165
+ throw new Error('missing_required_id');
166
+ return [id, options];
167
+ }
168
+ if (operation.kind === 'create') {
169
+ const data = { ...(toObjectOrUndefined(payload.data) ?? {}) };
170
+ if (!data || Object.keys(data).length === 0)
171
+ throw new Error('missing_required_data');
172
+ const scopeId = resolveScopeIdValue(payload);
173
+ if (scopeId && data.scopeId === undefined && isDocmanScopeOwnedCreateOperation(operation.operationId)) {
174
+ data.scopeId = scopeId;
175
+ }
176
+ return [data];
177
+ }
178
+ if (operation.kind === 'update') {
179
+ if (!id)
180
+ throw new Error('missing_required_id');
181
+ const patch = toObjectOrUndefined(payload.patch);
182
+ if (!patch || Object.keys(patch).length === 0)
183
+ throw new Error('missing_required_patch');
184
+ return [id, patch];
185
+ }
186
+ if (operation.kind === 'delete') {
187
+ if (!id)
188
+ throw new Error('missing_required_id');
189
+ return [id];
190
+ }
191
+ return [];
192
+ }
193
+ function buildCustomArgs(operation, payload) {
194
+ if (operation.args.length === 0)
195
+ return [];
196
+ if (DOCMAN_OBJECT_INPUT_CUSTOM_OPERATION_IDS.has(operation.operationId)) {
197
+ return [sanitizePayload(payload, operation)];
198
+ }
199
+ if (operation.args.length === 1) {
200
+ const [onlyArg] = operation.args;
201
+ if ((onlyArg.name === 'input' || onlyArg.name === 'payload') && payload[onlyArg.name] === undefined) {
202
+ return [sanitizePayload(payload, operation)];
203
+ }
204
+ }
205
+ const args = [];
206
+ for (const arg of operation.args) {
207
+ const value = resolveCustomArgValue(payload, arg);
208
+ if (value === undefined && !arg.optional) {
209
+ throw new Error(`missing_required_${toSnakeCase(arg.name)}`);
210
+ }
211
+ args.push(value);
212
+ }
213
+ return args;
214
+ }
215
+ function buildMethodArgs(operation, payload) {
216
+ if (operation.kind === 'custom')
217
+ return buildCustomArgs(operation, payload);
218
+ return buildCrudArgs(operation, payload);
219
+ }
220
+ function resolveOperationByToolId(toolId) {
221
+ const operation = getDocmanOperationContractByToolId(toolId);
222
+ if (operation)
223
+ return operation;
224
+ throw new Error(`unknown_docman_tool:${toolId}`);
225
+ }
226
+ function resolveOperationById(operationId) {
227
+ const operation = getDocmanOperationContractById(operationId);
228
+ if (operation)
229
+ return operation;
230
+ throw new Error(`unknown_docman_operation:${operationId}`);
231
+ }
232
+ async function runResolvedOperation(operation, input) {
233
+ const payload = toRecord(input);
234
+ if (operation.serviceKey === '__calls__') {
235
+ throw new Error(`docman_special_operation_missing:${operation.operationId}`);
236
+ }
237
+ const services = await getServices();
238
+ const service = services[operation.serviceKey];
239
+ if (!service)
240
+ throw new Error(`docman_service_not_found:${operation.serviceKey}`);
241
+ const method = service[operation.methodName];
242
+ if (typeof method !== 'function') {
243
+ throw new Error(`docman_method_missing:${operation.serviceKey}.${operation.methodName}`);
244
+ }
245
+ const args = buildMethodArgs(operation, payload);
246
+ const effectResult = method.apply(service, args);
247
+ return Effect.runPromise(effectResult);
248
+ }
249
+ export async function runDocmanKitOperationByToolId(toolId, input) {
250
+ const operation = resolveOperationByToolId(toolId);
251
+ return runResolvedOperation(operation, input);
252
+ }
253
+ export async function runDocmanKitOperationById(operationId, input) {
254
+ const operation = resolveOperationById(operationId);
255
+ return runResolvedOperation(operation, input);
256
+ }
257
+ export async function runDocmanKitOperationByTypedId(operationId, input) {
258
+ return runDocmanKitOperationById(operationId, input);
259
+ }
260
+ export async function runDocmanKitOperation(input, identifier) {
261
+ if ('toolId' in identifier) {
262
+ return runDocmanKitOperationByToolId(identifier.toolId, input);
263
+ }
264
+ const operation = resolveOperationById(identifier.operationId);
265
+ return runResolvedOperation(operation, input);
266
+ }
267
+ export function clearDocmanKitOperationCaches() {
268
+ cachedServices = null;
269
+ }
@@ -0,0 +1,11 @@
1
+ export type DocmanHostProjectionMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE';
2
+ export type DocmanHostRouteProjectionEntry = {
3
+ id: string;
4
+ method: DocmanHostProjectionMethod;
5
+ pattern: string;
6
+ operation: string;
7
+ summary?: string;
8
+ };
9
+ export declare function buildDocmanHostRouteProjection(options?: {
10
+ refresh?: boolean;
11
+ }): DocmanHostRouteProjectionEntry[];