@ankhorage/data-sources 0.6.0 → 1.0.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 (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +5 -5
  3. package/dist/discovery/index.d.ts +3 -3
  4. package/dist/discovery/index.d.ts.map +1 -1
  5. package/dist/discovery/index.js +19 -28
  6. package/dist/discovery/index.js.map +1 -1
  7. package/dist/generated-api/index.d.ts +10 -0
  8. package/dist/generated-api/index.d.ts.map +1 -0
  9. package/dist/generated-api/index.js +272 -0
  10. package/dist/generated-api/index.js.map +1 -0
  11. package/dist/graphql/index.d.ts +3 -3
  12. package/dist/graphql/index.d.ts.map +1 -1
  13. package/dist/graphql/index.js +24 -60
  14. package/dist/graphql/index.js.map +1 -1
  15. package/dist/index.d.ts +9 -3
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +17 -4
  18. package/dist/index.js.map +1 -1
  19. package/dist/openapi/index.d.ts +2 -2
  20. package/dist/openapi/index.d.ts.map +1 -1
  21. package/dist/openapi/index.js +19 -29
  22. package/dist/openapi/index.js.map +1 -1
  23. package/dist/rest/index.d.ts +3 -3
  24. package/dist/rest/index.d.ts.map +1 -1
  25. package/dist/rest/index.js +51 -51
  26. package/dist/rest/index.js.map +1 -1
  27. package/dist/test-runner/index.d.ts.map +1 -1
  28. package/dist/test-runner/index.js +60 -104
  29. package/dist/test-runner/index.js.map +1 -1
  30. package/package.json +2 -2
  31. package/src/discovery/index.test.ts +25 -101
  32. package/src/discovery/index.ts +22 -35
  33. package/src/{managed-api → generated-api}/index.test.ts +59 -67
  34. package/src/generated-api/index.ts +410 -0
  35. package/src/graphql/index.test.ts +28 -134
  36. package/src/graphql/index.ts +29 -89
  37. package/src/index.test.ts +25 -18
  38. package/src/index.ts +28 -7
  39. package/src/openapi/index.test.ts +36 -246
  40. package/src/openapi/index.ts +22 -60
  41. package/src/rest/index.test.ts +22 -173
  42. package/src/rest/index.ts +62 -59
  43. package/src/test-runner/index.test.ts +82 -150
  44. package/src/test-runner/index.ts +73 -143
  45. package/dist/managed-api/index.d.ts +0 -30
  46. package/dist/managed-api/index.d.ts.map +0 -1
  47. package/dist/managed-api/index.js +0 -257
  48. package/dist/managed-api/index.js.map +0 -1
  49. package/src/managed-api/index.ts +0 -364
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @ankhorage/data-sources
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - c540b7f: Adopt the canonical API source taxonomy, normalize generated API desired state directly from `@ankhorage/contracts`, and remove the legacy managed API source surface.
8
+
3
9
  ## 0.6.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/data-sources
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v0.5.5](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v0.6.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
7
7
 
8
8
  Provider-neutral data source and endpoint system for APIs, databases, and runtime bindings.
9
9
 
@@ -15,14 +15,14 @@ Provider-neutral data source and endpoint system for APIs, databases, and runtim
15
15
  - [Architecture overview](././paradox/diagrams/architecture-overview.mmd)
16
16
  - [Module relationships](././paradox/diagrams/module-relationships.mmd)
17
17
  - [Export graph](././paradox/diagrams/export-graph.mmd)
18
- - [createManagedApiResourceSchema sequence](././paradox/diagrams/sequences/create-managed-api-resource-schema.mmd)
18
+ - [createGeneratedApiResourceSchema sequence](././paradox/diagrams/sequences/create-generated-api-resource-schema.mmd)
19
19
  - [createManualRestDataSource sequence](././paradox/diagrams/sequences/create-manual-rest-data-source.mmd)
20
20
  - [createOpenApiDiscoveryCandidates sequence](././paradox/diagrams/sequences/create-open-api-discovery-candidates.mmd)
21
21
  - [normalizeGraphQlIntrospectionOperations sequence](././paradox/diagrams/sequences/normalize-graph-ql-introspection-operations.mmd)
22
22
  - [normalizeGraphQlIntrospectionSchemas sequence](././paradox/diagrams/sequences/normalize-graph-ql-introspection-schemas.mmd)
23
23
  - [normalizeManualRestDataSource sequence](././paradox/diagrams/sequences/normalize-manual-rest-data-source.mmd)
24
24
  - [normalizeOpenApiSchema sequence](././paradox/diagrams/sequences/normalize-open-api-schema.mmd)
25
- - [validateManagedApiDefinition sequence](././paradox/diagrams/sequences/validate-managed-api-definition.mmd)
25
+ - [validateGeneratedApiDefinition sequence](././paradox/diagrams/sequences/validate-generated-api-definition.mmd)
26
26
  - [validateManualRestDataSource sequence](././paradox/diagrams/sequences/validate-manual-rest-data-source.mmd)
27
27
 
28
28
  ## Architecture preview
@@ -44,11 +44,11 @@ graph TD
44
44
  package__ankhorage_data_sources -.-> module_src_discovery_index_ts
45
45
  module_src_discovery_index_ts --> module_src_graphql_index_ts
46
46
  module_src_discovery_index_ts --> module_src_openapi_index_ts
47
+ module_src_generated_api_index_ts["src/generated-api/index.ts"]
48
+ package__ankhorage_data_sources -.-> module_src_generated_api_index_ts
47
49
  module_src_graphql_index_ts["src/graphql/index.ts"]
48
50
  package__ankhorage_data_sources -.-> module_src_graphql_index_ts
49
51
  module_src_index_ts["src/index.ts"]
50
- module_src_managed_api_index_ts["src/managed-api/index.ts"]
51
- package__ankhorage_data_sources -.-> module_src_managed_api_index_ts
52
52
  module_src_openapi_index_ts["src/openapi/index.ts"]
53
53
  package__ankhorage_data_sources -.-> module_src_openapi_index_ts
54
54
  module_src_rest_index_ts["src/rest/index.ts"]
@@ -1,4 +1,4 @@
1
- import type { CredentialRef, DataContractValue, DataSourceDiagnostic, GraphQlDataSourceConfig, OpenApiDataSourceConfig } from '@ankhorage/contracts/data';
1
+ import type { CredentialRef, DataContractValue, DataSourceDiagnostic, ExternalGraphQlApiDataSourceConfig, ExternalRestApiDataSourceConfig } from '@ankhorage/contracts/data';
2
2
  export declare const DEFAULT_OPENAPI_DISCOVERY_PATHS: readonly ["openapi.json", "swagger.json", "api/openapi.json", "v3/api-docs"];
3
3
  export interface ExternalApiFetchInit {
4
4
  readonly method: 'GET' | 'POST';
@@ -29,7 +29,7 @@ export interface DiscoverOpenApiDataSourceInput {
29
29
  }
30
30
  export type DiscoverOpenApiDataSourceResult = {
31
31
  readonly ok: true;
32
- readonly data: OpenApiDataSourceConfig;
32
+ readonly data: ExternalRestApiDataSourceConfig;
33
33
  readonly documentUrl: string;
34
34
  readonly attempts: readonly OpenApiDiscoveryAttempt[];
35
35
  readonly diagnostics: readonly DataSourceDiagnostic[];
@@ -51,7 +51,7 @@ export interface IntrospectGraphQlDataSourceInput {
51
51
  }
52
52
  export type IntrospectGraphQlDataSourceResult = {
53
53
  readonly ok: true;
54
- readonly data: GraphQlDataSourceConfig;
54
+ readonly data: ExternalGraphQlApiDataSourceConfig;
55
55
  readonly diagnostics: readonly DataSourceDiagnostic[];
56
56
  } | {
57
57
  readonly ok: false;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"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"}
@@ -31,17 +31,17 @@ export async function discoverOpenApiDataSource(input) {
31
31
  for (const candidate of candidates) {
32
32
  const probed = await probeOpenApiCandidate(input, candidate);
33
33
  attempts.push(probed.attempt);
34
- if (probed.result !== undefined) {
35
- return probed.result.ok
36
- ? {
37
- ok: true,
38
- data: probed.result.data,
39
- documentUrl: candidate,
40
- attempts,
41
- diagnostics: probed.result.diagnostics ?? [],
42
- }
43
- : { ok: false, attempts, diagnostics: probed.result.diagnostics };
44
- }
34
+ if (probed.result === undefined)
35
+ continue;
36
+ return probed.result.ok
37
+ ? {
38
+ ok: true,
39
+ data: probed.result.data,
40
+ documentUrl: candidate,
41
+ attempts,
42
+ diagnostics: probed.result.diagnostics ?? [],
43
+ }
44
+ : { ok: false, attempts, diagnostics: probed.result.diagnostics };
45
45
  }
46
46
  return discoveryFailure(input.id, attempts, 'No supported OpenAPI document was found at the supplied URL or conventional locations.');
47
47
  }
@@ -50,7 +50,6 @@ export async function introspectGraphQlDataSource(input) {
50
50
  if (endpoint === undefined) {
51
51
  return graphqlFailure(input.id, 'GraphQL introspection requires a valid HTTP or HTTPS URL.');
52
52
  }
53
- const request = createGraphQlIntrospectionRequest();
54
53
  let response;
55
54
  try {
56
55
  response = await input.fetch(normalizeCandidateUrl(endpoint), {
@@ -60,7 +59,7 @@ export async function introspectGraphQlDataSource(input) {
60
59
  'content-type': 'application/json',
61
60
  ...(input.headers ?? {}),
62
61
  },
63
- body: JSON.stringify(request),
62
+ body: JSON.stringify(createGraphQlIntrospectionRequest()),
64
63
  });
65
64
  }
66
65
  catch {
@@ -72,8 +71,7 @@ export async function introspectGraphQlDataSource(input) {
72
71
  status: response.status,
73
72
  };
74
73
  }
75
- const parsed = await parseJsonResponse(response);
76
- const introspection = readGraphQlIntrospection(parsed);
74
+ const introspection = readGraphQlIntrospection(await parseJsonResponse(response));
77
75
  if (introspection === undefined) {
78
76
  return graphqlFailure(input.id, 'GraphQL introspection response did not contain data.__schema.', 'parse-error');
79
77
  }
@@ -97,24 +95,18 @@ async function probeOpenApiCandidate(input, candidate) {
97
95
  try {
98
96
  response = await input.fetch(candidate, {
99
97
  method: 'GET',
100
- headers: {
101
- accept: 'application/json, application/vnd.oai.openapi+json',
102
- },
98
+ headers: { accept: 'application/json, application/vnd.oai.openapi+json' },
103
99
  });
104
100
  }
105
101
  catch {
106
102
  return { attempt: { url: candidate, outcome: 'network-error' } };
107
103
  }
108
104
  if (!isSuccessfulStatus(response.status)) {
109
- return {
110
- attempt: { url: candidate, outcome: 'http-error', status: response.status },
111
- };
105
+ return { attempt: { url: candidate, outcome: 'http-error', status: response.status } };
112
106
  }
113
107
  const parsed = await parseJsonResponse(response);
114
108
  if (parsed === undefined) {
115
- return {
116
- attempt: { url: candidate, outcome: 'parse-error', status: response.status },
117
- };
109
+ return { attempt: { url: candidate, outcome: 'parse-error', status: response.status } };
118
110
  }
119
111
  if (!isOpenApiDocument(parsed)) {
120
112
  return {
@@ -182,11 +174,10 @@ function readGraphQlIntrospection(value) {
182
174
  return undefined;
183
175
  return data;
184
176
  }
185
- function isRecord(value) {
186
- return typeof value === 'object' && value !== null && !Array.isArray(value);
187
- }
188
177
  function readRecord(value) {
189
- return isRecord(value) ? value : undefined;
178
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
179
+ ? value
180
+ : undefined;
190
181
  }
191
182
  function discoveryFailure(dataSourceId, attempts, message) {
192
183
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CredentialRef,\n DataContractValue,\n DataSourceDiagnostic,\n GraphQlDataSourceConfig,\n OpenApiDataSourceConfig,\n} from '@ankhorage/contracts/data';\n\nimport {\n createGraphQlDataSource,\n createGraphQlIntrospectionRequest,\n type GraphQlIntrospectionResult,\n} from '../graphql';\nimport { importOpenApiDocument, type OpenApiDocumentObject } from '../openapi';\n\nexport const DEFAULT_OPENAPI_DISCOVERY_PATHS = [\n 'openapi.json',\n 'swagger.json',\n 'api/openapi.json',\n 'v3/api-docs',\n] as const;\n\nexport interface ExternalApiFetchInit {\n readonly method: 'GET' | 'POST';\n readonly headers: Readonly<Record<string, string>>;\n readonly body?: string;\n}\n\nexport interface ExternalApiFetchResponse {\n readonly status: number;\n text(): Promise<string>;\n}\n\nexport type ExternalApiFetch = (\n url: string,\n init: ExternalApiFetchInit,\n) => Promise<ExternalApiFetchResponse>;\n\nexport type OpenApiDiscoveryAttemptOutcome =\n | 'http-error'\n | 'invalid-document'\n | 'matched'\n | 'network-error'\n | 'parse-error'\n | 'unsupported-document';\n\nexport interface OpenApiDiscoveryAttempt {\n readonly url: string;\n readonly outcome: OpenApiDiscoveryAttemptOutcome;\n readonly status?: number;\n}\n\nexport interface DiscoverOpenApiDataSourceInput {\n readonly id: string;\n readonly url: string;\n readonly fetch: ExternalApiFetch;\n readonly baseUrl?: string;\n readonly credential?: CredentialRef;\n readonly name?: string;\n readonly description?: string;\n readonly metadata?: DataContractValue;\n readonly conventionalPaths?: readonly string[];\n}\n\nexport type DiscoverOpenApiDataSourceResult =\n | {\n readonly ok: true;\n readonly data: OpenApiDataSourceConfig;\n readonly documentUrl: string;\n readonly attempts: readonly OpenApiDiscoveryAttempt[];\n readonly diagnostics: readonly DataSourceDiagnostic[];\n }\n | {\n readonly ok: false;\n readonly attempts: readonly OpenApiDiscoveryAttempt[];\n readonly diagnostics: readonly DataSourceDiagnostic[];\n };\n\nexport interface IntrospectGraphQlDataSourceInput {\n readonly id: string;\n readonly endpointUrl: string;\n readonly fetch: ExternalApiFetch;\n readonly headers?: Readonly<Record<string, string>>;\n readonly credential?: CredentialRef;\n readonly name?: string;\n readonly description?: string;\n readonly schemaVersion?: string;\n readonly metadata?: DataContractValue;\n}\n\nexport type IntrospectGraphQlDataSourceResult =\n | {\n readonly ok: true;\n readonly data: GraphQlDataSourceConfig;\n readonly diagnostics: readonly DataSourceDiagnostic[];\n }\n | {\n readonly ok: false;\n readonly diagnostics: readonly DataSourceDiagnostic[];\n readonly status?: number;\n };\n\nexport function createOpenApiDiscoveryCandidates(\n rawUrl: string,\n conventionalPaths: readonly string[] = DEFAULT_OPENAPI_DISCOVERY_PATHS,\n): readonly string[] {\n const parsed = parseHttpUrl(rawUrl);\n if (parsed === undefined) return [];\n\n const exact = normalizeCandidateUrl(parsed);\n const serviceBase = new URL(exact);\n if (!serviceBase.pathname.endsWith('/')) serviceBase.pathname = `${serviceBase.pathname}/`;\n\n const candidates = [exact];\n for (const path of conventionalPaths) {\n const normalizedPath = path.replace(/^\\/+/, '');\n candidates.push(normalizeCandidateUrl(new URL(normalizedPath, serviceBase)));\n candidates.push(normalizeCandidateUrl(new URL(normalizedPath, `${parsed.origin}/`)));\n }\n\n return [...new Set(candidates)];\n}\n\nexport async function discoverOpenApiDataSource(\n input: DiscoverOpenApiDataSourceInput,\n): Promise<DiscoverOpenApiDataSourceResult> {\n const candidates = createOpenApiDiscoveryCandidates(input.url, input.conventionalPaths);\n if (candidates.length === 0) {\n return discoveryFailure(input.id, [], 'OpenAPI discovery requires a valid HTTP or HTTPS URL.');\n }\n\n const attempts: OpenApiDiscoveryAttempt[] = [];\n for (const candidate of candidates) {\n const probed = await probeOpenApiCandidate(input, candidate);\n attempts.push(probed.attempt);\n if (probed.result !== undefined) {\n return probed.result.ok\n ? {\n ok: true,\n data: probed.result.data,\n documentUrl: candidate,\n attempts,\n diagnostics: probed.result.diagnostics ?? [],\n }\n : { ok: false, attempts, diagnostics: probed.result.diagnostics };\n }\n }\n\n return discoveryFailure(\n input.id,\n attempts,\n 'No supported OpenAPI document was found at the supplied URL or conventional locations.',\n );\n}\n\nexport async function introspectGraphQlDataSource(\n input: IntrospectGraphQlDataSourceInput,\n): Promise<IntrospectGraphQlDataSourceResult> {\n const endpoint = parseHttpUrl(input.endpointUrl);\n if (endpoint === undefined) {\n return graphqlFailure(input.id, 'GraphQL introspection requires a valid HTTP or HTTPS URL.');\n }\n\n const request = createGraphQlIntrospectionRequest();\n let response: ExternalApiFetchResponse;\n try {\n response = await input.fetch(normalizeCandidateUrl(endpoint), {\n method: 'POST',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n ...(input.headers ?? {}),\n },\n body: JSON.stringify(request),\n });\n } catch {\n return graphqlFailure(input.id, 'GraphQL introspection request failed.', 'network-error');\n }\n\n if (!isSuccessfulStatus(response.status)) {\n return {\n ...graphqlFailure(\n input.id,\n `GraphQL introspection returned HTTP status ${response.status}.`,\n 'network-error',\n ),\n status: response.status,\n };\n }\n\n const parsed = await parseJsonResponse(response);\n const introspection = readGraphQlIntrospection(parsed);\n if (introspection === undefined) {\n return graphqlFailure(\n input.id,\n 'GraphQL introspection response did not contain data.__schema.',\n 'parse-error',\n );\n }\n\n const result = createGraphQlDataSource({\n id: input.id,\n endpointUrl: normalizeCandidateUrl(endpoint),\n credential: input.credential,\n name: input.name,\n description: input.description,\n introspection,\n introspectionEnabled: true,\n schemaVersion: input.schemaVersion,\n metadata: input.metadata,\n });\n\n return result.ok\n ? { ok: true, data: result.data, diagnostics: result.diagnostics ?? [] }\n : { ok: false, diagnostics: result.diagnostics };\n}\n\ninterface OpenApiProbeResult {\n readonly attempt: OpenApiDiscoveryAttempt;\n readonly result?: ReturnType<typeof importOpenApiDocument>;\n}\n\nasync function probeOpenApiCandidate(\n input: DiscoverOpenApiDataSourceInput,\n candidate: string,\n): Promise<OpenApiProbeResult> {\n let response: ExternalApiFetchResponse;\n try {\n response = await input.fetch(candidate, {\n method: 'GET',\n headers: {\n accept: 'application/json, application/vnd.oai.openapi+json',\n },\n });\n } catch {\n return { attempt: { url: candidate, outcome: 'network-error' } };\n }\n\n if (!isSuccessfulStatus(response.status)) {\n return {\n attempt: { url: candidate, outcome: 'http-error', status: response.status },\n };\n }\n\n const parsed = await parseJsonResponse(response);\n if (parsed === undefined) {\n return {\n attempt: { url: candidate, outcome: 'parse-error', status: response.status },\n };\n }\n\n if (!isOpenApiDocument(parsed)) {\n return {\n attempt: { url: candidate, outcome: 'unsupported-document', status: response.status },\n };\n }\n\n const result = importOpenApiDocument({\n id: input.id,\n document: parsed,\n baseUrl: input.baseUrl,\n credential: input.credential,\n documentUrl: candidate,\n name: input.name,\n description: input.description,\n metadata: input.metadata,\n });\n\n return {\n attempt: {\n url: candidate,\n outcome: result.ok ? 'matched' : 'invalid-document',\n status: response.status,\n },\n result,\n };\n}\n\nfunction parseHttpUrl(rawUrl: string): URL | undefined {\n try {\n const parsed = new URL(rawUrl.trim());\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return undefined;\n if (parsed.username.length > 0 || parsed.password.length > 0) return undefined;\n return parsed;\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeCandidateUrl(url: URL): string {\n const normalized = new URL(url.toString());\n normalized.hash = '';\n return normalized.toString();\n}\n\nfunction isSuccessfulStatus(status: number): boolean {\n return status >= 200 && status < 300;\n}\n\nasync function parseJsonResponse(response: ExternalApiFetchResponse): Promise<unknown> {\n try {\n return JSON.parse(await response.text()) as unknown;\n } catch {\n return undefined;\n }\n}\n\nfunction isOpenApiDocument(value: unknown): value is OpenApiDocumentObject {\n const record = readRecord(value);\n return (\n record !== undefined &&\n typeof record.openapi === 'string' &&\n readRecord(record.paths) !== undefined\n );\n}\n\nfunction readGraphQlIntrospection(value: unknown): GraphQlIntrospectionResult | undefined {\n const payload = readRecord(value);\n const data = readRecord(payload?.data);\n if (readRecord(data?.__schema) === undefined) return undefined;\n return data;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction readRecord(value: unknown): Readonly<Record<string, unknown>> | undefined {\n return isRecord(value) ? value : undefined;\n}\n\nfunction discoveryFailure(\n dataSourceId: string,\n attempts: readonly OpenApiDiscoveryAttempt[],\n message: string,\n): DiscoverOpenApiDataSourceResult {\n return {\n ok: false,\n attempts,\n diagnostics: [{ code: 'missing-schema', dataSourceId, message, severity: 'error' }],\n };\n}\n\nfunction graphqlFailure(\n dataSourceId: string,\n message: string,\n code: DataSourceDiagnostic['code'] = 'invalid-config',\n): Extract<IntrospectGraphQlDataSourceResult, { readonly ok: false }> {\n return {\n ok: false,\n diagnostics: [{ code, dataSourceId, message, severity: 'error' }],\n };\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CredentialRef,\n DataContractValue,\n DataSourceDiagnostic,\n ExternalGraphQlApiDataSourceConfig,\n ExternalRestApiDataSourceConfig,\n} from '@ankhorage/contracts/data';\n\nimport {\n createGraphQlDataSource,\n createGraphQlIntrospectionRequest,\n type GraphQlIntrospectionResult,\n} from '../graphql';\nimport { importOpenApiDocument, type OpenApiDocumentObject } from '../openapi';\n\nexport const DEFAULT_OPENAPI_DISCOVERY_PATHS = [\n 'openapi.json',\n 'swagger.json',\n 'api/openapi.json',\n 'v3/api-docs',\n] as const;\n\nexport interface ExternalApiFetchInit {\n readonly method: 'GET' | 'POST';\n readonly headers: Readonly<Record<string, string>>;\n readonly body?: string;\n}\n\nexport interface ExternalApiFetchResponse {\n readonly status: number;\n text(): Promise<string>;\n}\n\nexport type ExternalApiFetch = (\n url: string,\n init: ExternalApiFetchInit,\n) => Promise<ExternalApiFetchResponse>;\n\nexport type OpenApiDiscoveryAttemptOutcome =\n | 'http-error'\n | 'invalid-document'\n | 'matched'\n | 'network-error'\n | 'parse-error'\n | 'unsupported-document';\n\nexport interface OpenApiDiscoveryAttempt {\n readonly url: string;\n readonly outcome: OpenApiDiscoveryAttemptOutcome;\n readonly status?: number;\n}\n\nexport interface DiscoverOpenApiDataSourceInput {\n readonly id: string;\n readonly url: string;\n readonly fetch: ExternalApiFetch;\n readonly baseUrl?: string;\n readonly credential?: CredentialRef;\n readonly name?: string;\n readonly description?: string;\n readonly metadata?: DataContractValue;\n readonly conventionalPaths?: readonly string[];\n}\n\nexport type DiscoverOpenApiDataSourceResult =\n | {\n readonly ok: true;\n readonly data: ExternalRestApiDataSourceConfig;\n readonly documentUrl: string;\n readonly attempts: readonly OpenApiDiscoveryAttempt[];\n readonly diagnostics: readonly DataSourceDiagnostic[];\n }\n | {\n readonly ok: false;\n readonly attempts: readonly OpenApiDiscoveryAttempt[];\n readonly diagnostics: readonly DataSourceDiagnostic[];\n };\n\nexport interface IntrospectGraphQlDataSourceInput {\n readonly id: string;\n readonly endpointUrl: string;\n readonly fetch: ExternalApiFetch;\n readonly headers?: Readonly<Record<string, string>>;\n readonly credential?: CredentialRef;\n readonly name?: string;\n readonly description?: string;\n readonly schemaVersion?: string;\n readonly metadata?: DataContractValue;\n}\n\nexport type IntrospectGraphQlDataSourceResult =\n | {\n readonly ok: true;\n readonly data: ExternalGraphQlApiDataSourceConfig;\n readonly diagnostics: readonly DataSourceDiagnostic[];\n }\n | {\n readonly ok: false;\n readonly diagnostics: readonly DataSourceDiagnostic[];\n readonly status?: number;\n };\n\nexport function createOpenApiDiscoveryCandidates(\n rawUrl: string,\n conventionalPaths: readonly string[] = DEFAULT_OPENAPI_DISCOVERY_PATHS,\n): readonly string[] {\n const parsed = parseHttpUrl(rawUrl);\n if (parsed === undefined) return [];\n\n const exact = normalizeCandidateUrl(parsed);\n const serviceBase = new URL(exact);\n if (!serviceBase.pathname.endsWith('/')) serviceBase.pathname = `${serviceBase.pathname}/`;\n\n const candidates = [exact];\n for (const path of conventionalPaths) {\n const normalizedPath = path.replace(/^\\/+/, '');\n candidates.push(normalizeCandidateUrl(new URL(normalizedPath, serviceBase)));\n candidates.push(normalizeCandidateUrl(new URL(normalizedPath, `${parsed.origin}/`)));\n }\n\n return [...new Set(candidates)];\n}\n\nexport async function discoverOpenApiDataSource(\n input: DiscoverOpenApiDataSourceInput,\n): Promise<DiscoverOpenApiDataSourceResult> {\n const candidates = createOpenApiDiscoveryCandidates(input.url, input.conventionalPaths);\n if (candidates.length === 0) {\n return discoveryFailure(input.id, [], 'OpenAPI discovery requires a valid HTTP or HTTPS URL.');\n }\n\n const attempts: OpenApiDiscoveryAttempt[] = [];\n for (const candidate of candidates) {\n const probed = await probeOpenApiCandidate(input, candidate);\n attempts.push(probed.attempt);\n if (probed.result === undefined) continue;\n return probed.result.ok\n ? {\n ok: true,\n data: probed.result.data,\n documentUrl: candidate,\n attempts,\n diagnostics: probed.result.diagnostics ?? [],\n }\n : { ok: false, attempts, diagnostics: probed.result.diagnostics };\n }\n\n return discoveryFailure(\n input.id,\n attempts,\n 'No supported OpenAPI document was found at the supplied URL or conventional locations.',\n );\n}\n\nexport async function introspectGraphQlDataSource(\n input: IntrospectGraphQlDataSourceInput,\n): Promise<IntrospectGraphQlDataSourceResult> {\n const endpoint = parseHttpUrl(input.endpointUrl);\n if (endpoint === undefined) {\n return graphqlFailure(input.id, 'GraphQL introspection requires a valid HTTP or HTTPS URL.');\n }\n\n let response: ExternalApiFetchResponse;\n try {\n response = await input.fetch(normalizeCandidateUrl(endpoint), {\n method: 'POST',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n ...(input.headers ?? {}),\n },\n body: JSON.stringify(createGraphQlIntrospectionRequest()),\n });\n } catch {\n return graphqlFailure(input.id, 'GraphQL introspection request failed.', 'network-error');\n }\n\n if (!isSuccessfulStatus(response.status)) {\n return {\n ...graphqlFailure(\n input.id,\n `GraphQL introspection returned HTTP status ${response.status}.`,\n 'network-error',\n ),\n status: response.status,\n };\n }\n\n const introspection = readGraphQlIntrospection(await parseJsonResponse(response));\n if (introspection === undefined) {\n return graphqlFailure(\n input.id,\n 'GraphQL introspection response did not contain data.__schema.',\n 'parse-error',\n );\n }\n\n const result = createGraphQlDataSource({\n id: input.id,\n endpointUrl: normalizeCandidateUrl(endpoint),\n credential: input.credential,\n name: input.name,\n description: input.description,\n introspection,\n introspectionEnabled: true,\n schemaVersion: input.schemaVersion,\n metadata: input.metadata,\n });\n\n return result.ok\n ? { ok: true, data: result.data, diagnostics: result.diagnostics ?? [] }\n : { ok: false, diagnostics: result.diagnostics };\n}\n\ninterface OpenApiProbeResult {\n readonly attempt: OpenApiDiscoveryAttempt;\n readonly result?: ReturnType<typeof importOpenApiDocument>;\n}\n\nasync function probeOpenApiCandidate(\n input: DiscoverOpenApiDataSourceInput,\n candidate: string,\n): Promise<OpenApiProbeResult> {\n let response: ExternalApiFetchResponse;\n try {\n response = await input.fetch(candidate, {\n method: 'GET',\n headers: { accept: 'application/json, application/vnd.oai.openapi+json' },\n });\n } catch {\n return { attempt: { url: candidate, outcome: 'network-error' } };\n }\n\n if (!isSuccessfulStatus(response.status)) {\n return { attempt: { url: candidate, outcome: 'http-error', status: response.status } };\n }\n\n const parsed = await parseJsonResponse(response);\n if (parsed === undefined) {\n return { attempt: { url: candidate, outcome: 'parse-error', status: response.status } };\n }\n if (!isOpenApiDocument(parsed)) {\n return {\n attempt: { url: candidate, outcome: 'unsupported-document', status: response.status },\n };\n }\n\n const result = importOpenApiDocument({\n id: input.id,\n document: parsed,\n baseUrl: input.baseUrl,\n credential: input.credential,\n documentUrl: candidate,\n name: input.name,\n description: input.description,\n metadata: input.metadata,\n });\n return {\n attempt: {\n url: candidate,\n outcome: result.ok ? 'matched' : 'invalid-document',\n status: response.status,\n },\n result,\n };\n}\n\nfunction parseHttpUrl(rawUrl: string): URL | undefined {\n try {\n const parsed = new URL(rawUrl.trim());\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return undefined;\n if (parsed.username.length > 0 || parsed.password.length > 0) return undefined;\n return parsed;\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeCandidateUrl(url: URL): string {\n const normalized = new URL(url.toString());\n normalized.hash = '';\n return normalized.toString();\n}\n\nfunction isSuccessfulStatus(status: number): boolean {\n return status >= 200 && status < 300;\n}\n\nasync function parseJsonResponse(response: ExternalApiFetchResponse): Promise<unknown> {\n try {\n return JSON.parse(await response.text()) as unknown;\n } catch {\n return undefined;\n }\n}\n\nfunction isOpenApiDocument(value: unknown): value is OpenApiDocumentObject {\n const record = readRecord(value);\n return (\n record !== undefined &&\n typeof record.openapi === 'string' &&\n readRecord(record.paths) !== undefined\n );\n}\n\nfunction readGraphQlIntrospection(value: unknown): GraphQlIntrospectionResult | undefined {\n const payload = readRecord(value);\n const data = readRecord(payload?.data);\n if (readRecord(data?.__schema) === undefined) return undefined;\n return data;\n}\n\nfunction readRecord(value: unknown): Readonly<Record<string, unknown>> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Readonly<Record<string, unknown>>)\n : undefined;\n}\n\nfunction discoveryFailure(\n dataSourceId: string,\n attempts: readonly OpenApiDiscoveryAttempt[],\n message: string,\n): DiscoverOpenApiDataSourceResult {\n return {\n ok: false,\n attempts,\n diagnostics: [{ code: 'missing-schema', dataSourceId, message, severity: 'error' }],\n };\n}\n\nfunction graphqlFailure(\n dataSourceId: string,\n message: string,\n code: DataSourceDiagnostic['code'] = 'invalid-config',\n): Extract<IntrospectGraphQlDataSourceResult, { readonly ok: false }> {\n return {\n ok: false,\n diagnostics: [{ code, dataSourceId, message, severity: 'error' }],\n };\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { DataEndpointConfig, DataOperationConfig, DataSchema, DataSourceDiagnostic, DataSourceDiagnosticResult, GeneratedApiCrudOperation, GeneratedApiDefinition, GeneratedApiResourceDefinition, GeneratedRestApiDataSourceConfig, OperationId } from '@ankhorage/contracts/data';
2
+ import type { DbCollectionDefinition } from '@ankhorage/contracts/db';
3
+ export declare function createGeneratedApiDataSource(definition: GeneratedApiDefinition): DataSourceDiagnosticResult<GeneratedRestApiDataSourceConfig>;
4
+ export declare function validateGeneratedApiDefinition(definition: GeneratedApiDefinition): readonly DataSourceDiagnostic[];
5
+ export declare function normalizeGeneratedApiDataSource(definition: GeneratedApiDefinition): GeneratedRestApiDataSourceConfig;
6
+ export declare function createGeneratedApiEndpoint(definition: GeneratedApiDefinition, resource: GeneratedApiResourceDefinition): DataEndpointConfig;
7
+ export declare function createGeneratedApiOperation(definition: GeneratedApiDefinition, resource: GeneratedApiResourceDefinition, operation: GeneratedApiCrudOperation): DataOperationConfig;
8
+ export declare function createGeneratedApiOperationId(resourceId: string, operation: GeneratedApiCrudOperation): OperationId;
9
+ export declare function createGeneratedApiResourceSchema(collection: DbCollectionDefinition): DataSchema;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generated-api/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAClB,mBAAmB,EAGnB,UAAU,EACV,oBAAoB,EACpB,0BAA0B,EAE1B,yBAAyB,EACzB,sBAAsB,EAEtB,8BAA8B,EAC9B,gCAAgC,EAChC,WAAW,EACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACV,sBAAsB,EAGvB,MAAM,yBAAyB,CAAC;AAEjC,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,sBAAsB,GACjC,0BAA0B,CAAC,gCAAgC,CAAC,CAW9D;AAED,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,sBAAsB,GACjC,SAAS,oBAAoB,EAAE,CA6BjC;AAED,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,sBAAsB,GACjC,gCAAgC,CAsBlC;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,sBAAsB,EAClC,QAAQ,EAAE,8BAA8B,GACvC,kBAAkB,CAiBpB;AAED,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,sBAAsB,EAClC,QAAQ,EAAE,8BAA8B,EACxC,SAAS,EAAE,yBAAyB,GACnC,mBAAmB,CAqBrB;AAED,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,yBAAyB,GACnC,WAAW,CAEb;AAED,wBAAgB,gCAAgC,CAAC,UAAU,EAAE,sBAAsB,GAAG,UAAU,CAe/F"}
@@ -0,0 +1,272 @@
1
+ export function createGeneratedApiDataSource(definition) {
2
+ const diagnostics = validateGeneratedApiDefinition(definition);
3
+ if (diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
4
+ return { ok: false, diagnostics };
5
+ }
6
+ return {
7
+ ok: true,
8
+ data: normalizeGeneratedApiDataSource(definition),
9
+ diagnostics,
10
+ };
11
+ }
12
+ export function validateGeneratedApiDefinition(definition) {
13
+ const diagnostics = [];
14
+ const resourceIds = new Set();
15
+ if (definition.basePath.trim().length === 0 || !definition.basePath.startsWith('/')) {
16
+ diagnostics.push({
17
+ code: 'invalid-config',
18
+ dataSourceId: definition.id,
19
+ message: 'Generated API basePath must be a non-empty absolute path.',
20
+ path: 'basePath',
21
+ severity: 'error',
22
+ });
23
+ }
24
+ if (definition.resources.length === 0) {
25
+ diagnostics.push({
26
+ code: 'invalid-config',
27
+ dataSourceId: definition.id,
28
+ message: 'Generated API requires at least one resource.',
29
+ path: 'resources',
30
+ severity: 'error',
31
+ });
32
+ }
33
+ for (const resource of definition.resources) {
34
+ validateGeneratedApiResource(definition, resource, resourceIds, diagnostics);
35
+ }
36
+ return diagnostics;
37
+ }
38
+ export function normalizeGeneratedApiDataSource(definition) {
39
+ const endpoints = {};
40
+ const schemas = {};
41
+ for (const resource of definition.resources) {
42
+ endpoints[resource.id] = createGeneratedApiEndpoint(definition, resource);
43
+ schemas[resource.id] = createGeneratedApiResourceSchema(resource.collection);
44
+ }
45
+ return {
46
+ id: definition.id,
47
+ kind: 'api',
48
+ origin: 'generated',
49
+ protocol: 'rest',
50
+ generatedApiId: definition.id,
51
+ name: definition.name,
52
+ description: definition.description,
53
+ adapter: definition.database,
54
+ endpoints,
55
+ schemas,
56
+ metadata: createGeneratedApiSourceMetadata(definition),
57
+ };
58
+ }
59
+ export function createGeneratedApiEndpoint(definition, resource) {
60
+ const operations = {};
61
+ for (const operation of resource.operations) {
62
+ const config = createGeneratedApiOperation(definition, resource, operation);
63
+ operations[config.id] = config;
64
+ }
65
+ return {
66
+ id: resource.id,
67
+ kind: 'database',
68
+ name: resource.name,
69
+ description: resource.description,
70
+ path: resource.path,
71
+ operations,
72
+ metadata: createGeneratedApiResourceMetadata(definition, resource),
73
+ };
74
+ }
75
+ export function createGeneratedApiOperation(definition, resource, operation) {
76
+ const operationId = createGeneratedApiOperationId(resource.id, operation);
77
+ const primaryKey = resolveGeneratedApiPrimaryKey(resource.collection);
78
+ const primaryKeyParameter = primaryKey === undefined ? undefined : createPrimaryKeyParameter(primaryKey);
79
+ return {
80
+ id: operationId,
81
+ endpointId: resource.id,
82
+ name: createGeneratedApiOperationName(resource, operation),
83
+ protocol: 'database',
84
+ intent: mapGeneratedApiOperationIntent(operation),
85
+ path: resource.path,
86
+ request: createGeneratedApiOperationRequest(resource.collection, operation, primaryKeyParameter),
87
+ response: createGeneratedApiOperationResponse(resource.collection, operation),
88
+ metadata: createGeneratedApiOperationMetadata(definition, resource, operation),
89
+ };
90
+ }
91
+ export function createGeneratedApiOperationId(resourceId, operation) {
92
+ return `${resourceId}.${operation}`;
93
+ }
94
+ export function createGeneratedApiResourceSchema(collection) {
95
+ const properties = {};
96
+ const required = [];
97
+ for (const field of collection.fields) {
98
+ properties[field.name] = createGeneratedApiFieldSchema(field);
99
+ if (field.required === true)
100
+ required.push(field.name);
101
+ }
102
+ return {
103
+ type: 'object',
104
+ title: collection.name,
105
+ required: required.length > 0 ? required : undefined,
106
+ properties,
107
+ };
108
+ }
109
+ function validateGeneratedApiResource(definition, resource, resourceIds, diagnostics) {
110
+ if (resource.id.trim().length === 0) {
111
+ diagnostics.push(resourceDiagnostic(definition, resource, 'Resource IDs must be non-empty.', 'id'));
112
+ }
113
+ else if (resourceIds.has(resource.id)) {
114
+ diagnostics.push(resourceDiagnostic(definition, resource, `Resource ID '${resource.id}' is duplicated.`, 'id'));
115
+ }
116
+ resourceIds.add(resource.id);
117
+ if (!resource.path.startsWith('/')) {
118
+ diagnostics.push(resourceDiagnostic(definition, resource, 'Generated API resource paths must start with `/`.', 'path'));
119
+ }
120
+ if (resource.collection.name.trim().length === 0) {
121
+ diagnostics.push(resourceDiagnostic(definition, resource, 'Generated API resource collections must have a non-empty name.', 'collection.name'));
122
+ }
123
+ if (resource.operations.length === 0) {
124
+ diagnostics.push(resourceDiagnostic(definition, resource, 'Generated API resources require at least one operation.', 'operations'));
125
+ }
126
+ const primaryKey = resolveGeneratedApiPrimaryKey(resource.collection);
127
+ if (primaryKey === undefined &&
128
+ resource.operations.some((operation) => operation !== 'list' && operation !== 'create')) {
129
+ diagnostics.push(resourceDiagnostic(definition, resource, 'Generated API resources need a primaryKey field for read/update/delete operations.', 'collection.primaryKey'));
130
+ }
131
+ }
132
+ function resourceDiagnostic(definition, resource, message, path) {
133
+ return {
134
+ code: 'invalid-config',
135
+ dataSourceId: definition.id,
136
+ endpointId: resource.id,
137
+ message,
138
+ path: `resources.${resource.id}.${path}`,
139
+ severity: 'error',
140
+ };
141
+ }
142
+ function createGeneratedApiSourceMetadata(definition) {
143
+ return {
144
+ ...readMetadataRecord(definition.metadata),
145
+ basePath: definition.basePath,
146
+ source: 'generated-api',
147
+ auth: createGeneratedApiAuthMetadata(definition.auth),
148
+ };
149
+ }
150
+ function createGeneratedApiResourceMetadata(definition, resource) {
151
+ return {
152
+ ...readMetadataRecord(resource.metadata),
153
+ adapterId: definition.database.id,
154
+ collection: resource.collection.name,
155
+ schema: resource.collection.schema ?? null,
156
+ source: 'generated-api',
157
+ seed: resource.seed ?? [],
158
+ policies: createGeneratedApiPolicyMetadata(resource.policies),
159
+ };
160
+ }
161
+ function createGeneratedApiOperationMetadata(definition, resource, operation) {
162
+ return {
163
+ adapterId: definition.database.id,
164
+ collection: resource.collection.name,
165
+ operation,
166
+ schema: resource.collection.schema ?? null,
167
+ source: 'generated-api',
168
+ policies: createGeneratedApiPolicyMetadata(resource.policies?.filter((policy) => policy.operation === undefined || policy.operation === operation)),
169
+ };
170
+ }
171
+ function createGeneratedApiAuthMetadata(auth) {
172
+ if (auth === undefined)
173
+ return null;
174
+ const metadata = {};
175
+ if (auth.required !== undefined)
176
+ metadata.required = auth.required;
177
+ if (auth.roles !== undefined)
178
+ metadata.roles = auth.roles;
179
+ if (auth.permissions !== undefined)
180
+ metadata.permissions = auth.permissions;
181
+ if (auth.policy !== undefined)
182
+ metadata.policy = auth.policy;
183
+ if (auth.metadata !== undefined)
184
+ metadata.metadata = auth.metadata;
185
+ return metadata;
186
+ }
187
+ function createGeneratedApiPolicyMetadata(policies) {
188
+ return (policies ?? []).map((policy) => {
189
+ const metadata = { id: policy.id };
190
+ if (policy.operation !== undefined)
191
+ metadata.operation = policy.operation;
192
+ return metadata;
193
+ });
194
+ }
195
+ function createGeneratedApiOperationName(resource, operation) {
196
+ return `${operation} ${resource.name ?? resource.id}`;
197
+ }
198
+ function mapGeneratedApiOperationIntent(operation) {
199
+ if (operation === 'list' || operation === 'read')
200
+ return 'read';
201
+ if (operation === 'create')
202
+ return 'create';
203
+ if (operation === 'update')
204
+ return 'update';
205
+ return 'delete';
206
+ }
207
+ function createGeneratedApiOperationRequest(collection, operation, primaryKeyParameter) {
208
+ if (operation === 'list') {
209
+ return {
210
+ parameters: [
211
+ { name: 'limit', location: 'query', schema: { type: 'integer' } },
212
+ { name: 'offset', location: 'query', schema: { type: 'integer' } },
213
+ ],
214
+ };
215
+ }
216
+ if (operation === 'read' || operation === 'delete') {
217
+ return primaryKeyParameter === undefined ? undefined : { parameters: [primaryKeyParameter] };
218
+ }
219
+ const parameters = operation === 'update' && primaryKeyParameter !== undefined ? [primaryKeyParameter] : undefined;
220
+ return {
221
+ parameters,
222
+ schema: createGeneratedApiResourceSchema(collection),
223
+ };
224
+ }
225
+ function createGeneratedApiOperationResponse(collection, operation) {
226
+ const schema = createGeneratedApiResourceSchema(collection);
227
+ if (operation === 'list')
228
+ return { schema: { type: 'array', items: schema } };
229
+ if (operation === 'delete') {
230
+ return { schema: { type: 'object', properties: { deleted: { type: 'boolean' } } } };
231
+ }
232
+ return { schema };
233
+ }
234
+ function createPrimaryKeyParameter(primaryKey) {
235
+ return {
236
+ name: primaryKey,
237
+ location: 'path',
238
+ required: true,
239
+ schema: { type: 'string' },
240
+ };
241
+ }
242
+ function resolveGeneratedApiPrimaryKey(collection) {
243
+ return collection.primaryKey ?? collection.fields.find((field) => field.unique === true)?.name;
244
+ }
245
+ function createGeneratedApiFieldSchema(field) {
246
+ return {
247
+ ...mapDbFieldTypeToDataSchema(field.type),
248
+ default: field.defaultValue,
249
+ };
250
+ }
251
+ function mapDbFieldTypeToDataSchema(type) {
252
+ if (type === 'boolean')
253
+ return { type: 'boolean' };
254
+ if (type === 'datetime')
255
+ return { type: 'string', format: 'date-time' };
256
+ if (type === 'json')
257
+ return { type: 'object' };
258
+ if (type === 'number')
259
+ return { type: 'number' };
260
+ if (type === 'uuid')
261
+ return { type: 'string', format: 'uuid' };
262
+ return { type: 'string' };
263
+ }
264
+ function readMetadataRecord(value) {
265
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
266
+ return {};
267
+ const metadata = {};
268
+ for (const [key, item] of Object.entries(value))
269
+ metadata[key] = item;
270
+ return metadata;
271
+ }
272
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generated-api/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DataContractValue,\n DataEndpointConfig,\n DataOperationConfig,\n DataOperationIntent,\n DataOperationParameter,\n DataSchema,\n DataSourceDiagnostic,\n DataSourceDiagnosticResult,\n GeneratedApiAuthRequirement,\n GeneratedApiCrudOperation,\n GeneratedApiDefinition,\n GeneratedApiOperationPolicyRef,\n GeneratedApiResourceDefinition,\n GeneratedRestApiDataSourceConfig,\n OperationId,\n} from '@ankhorage/contracts/data';\nimport type {\n DbCollectionDefinition,\n DbFieldDefinition,\n DbFieldType,\n} from '@ankhorage/contracts/db';\n\nexport function createGeneratedApiDataSource(\n definition: GeneratedApiDefinition,\n): DataSourceDiagnosticResult<GeneratedRestApiDataSourceConfig> {\n const diagnostics = validateGeneratedApiDefinition(definition);\n if (diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {\n return { ok: false, diagnostics };\n }\n\n return {\n ok: true,\n data: normalizeGeneratedApiDataSource(definition),\n diagnostics,\n };\n}\n\nexport function validateGeneratedApiDefinition(\n definition: GeneratedApiDefinition,\n): readonly DataSourceDiagnostic[] {\n const diagnostics: DataSourceDiagnostic[] = [];\n const resourceIds = new Set<string>();\n\n if (definition.basePath.trim().length === 0 || !definition.basePath.startsWith('/')) {\n diagnostics.push({\n code: 'invalid-config',\n dataSourceId: definition.id,\n message: 'Generated API basePath must be a non-empty absolute path.',\n path: 'basePath',\n severity: 'error',\n });\n }\n\n if (definition.resources.length === 0) {\n diagnostics.push({\n code: 'invalid-config',\n dataSourceId: definition.id,\n message: 'Generated API requires at least one resource.',\n path: 'resources',\n severity: 'error',\n });\n }\n\n for (const resource of definition.resources) {\n validateGeneratedApiResource(definition, resource, resourceIds, diagnostics);\n }\n\n return diagnostics;\n}\n\nexport function normalizeGeneratedApiDataSource(\n definition: GeneratedApiDefinition,\n): GeneratedRestApiDataSourceConfig {\n const endpoints: Record<string, DataEndpointConfig> = {};\n const schemas: Record<string, DataSchema> = {};\n\n for (const resource of definition.resources) {\n endpoints[resource.id] = createGeneratedApiEndpoint(definition, resource);\n schemas[resource.id] = createGeneratedApiResourceSchema(resource.collection);\n }\n\n return {\n id: definition.id,\n kind: 'api',\n origin: 'generated',\n protocol: 'rest',\n generatedApiId: definition.id,\n name: definition.name,\n description: definition.description,\n adapter: definition.database,\n endpoints,\n schemas,\n metadata: createGeneratedApiSourceMetadata(definition),\n };\n}\n\nexport function createGeneratedApiEndpoint(\n definition: GeneratedApiDefinition,\n resource: GeneratedApiResourceDefinition,\n): DataEndpointConfig {\n const operations: Record<OperationId, DataOperationConfig> = {};\n\n for (const operation of resource.operations) {\n const config = createGeneratedApiOperation(definition, resource, operation);\n operations[config.id] = config;\n }\n\n return {\n id: resource.id,\n kind: 'database',\n name: resource.name,\n description: resource.description,\n path: resource.path,\n operations,\n metadata: createGeneratedApiResourceMetadata(definition, resource),\n };\n}\n\nexport function createGeneratedApiOperation(\n definition: GeneratedApiDefinition,\n resource: GeneratedApiResourceDefinition,\n operation: GeneratedApiCrudOperation,\n): DataOperationConfig {\n const operationId = createGeneratedApiOperationId(resource.id, operation);\n const primaryKey = resolveGeneratedApiPrimaryKey(resource.collection);\n const primaryKeyParameter =\n primaryKey === undefined ? undefined : createPrimaryKeyParameter(primaryKey);\n\n return {\n id: operationId,\n endpointId: resource.id,\n name: createGeneratedApiOperationName(resource, operation),\n protocol: 'database',\n intent: mapGeneratedApiOperationIntent(operation),\n path: resource.path,\n request: createGeneratedApiOperationRequest(\n resource.collection,\n operation,\n primaryKeyParameter,\n ),\n response: createGeneratedApiOperationResponse(resource.collection, operation),\n metadata: createGeneratedApiOperationMetadata(definition, resource, operation),\n };\n}\n\nexport function createGeneratedApiOperationId(\n resourceId: string,\n operation: GeneratedApiCrudOperation,\n): OperationId {\n return `${resourceId}.${operation}`;\n}\n\nexport function createGeneratedApiResourceSchema(collection: DbCollectionDefinition): DataSchema {\n const properties: Record<string, DataSchema> = {};\n const required: string[] = [];\n\n for (const field of collection.fields) {\n properties[field.name] = createGeneratedApiFieldSchema(field);\n if (field.required === true) required.push(field.name);\n }\n\n return {\n type: 'object',\n title: collection.name,\n required: required.length > 0 ? required : undefined,\n properties,\n };\n}\n\nfunction validateGeneratedApiResource(\n definition: GeneratedApiDefinition,\n resource: GeneratedApiResourceDefinition,\n resourceIds: Set<string>,\n diagnostics: DataSourceDiagnostic[],\n): void {\n if (resource.id.trim().length === 0) {\n diagnostics.push(\n resourceDiagnostic(definition, resource, 'Resource IDs must be non-empty.', 'id'),\n );\n } else if (resourceIds.has(resource.id)) {\n diagnostics.push(\n resourceDiagnostic(definition, resource, `Resource ID '${resource.id}' is duplicated.`, 'id'),\n );\n }\n resourceIds.add(resource.id);\n\n if (!resource.path.startsWith('/')) {\n diagnostics.push(\n resourceDiagnostic(\n definition,\n resource,\n 'Generated API resource paths must start with `/`.',\n 'path',\n ),\n );\n }\n\n if (resource.collection.name.trim().length === 0) {\n diagnostics.push(\n resourceDiagnostic(\n definition,\n resource,\n 'Generated API resource collections must have a non-empty name.',\n 'collection.name',\n ),\n );\n }\n\n if (resource.operations.length === 0) {\n diagnostics.push(\n resourceDiagnostic(\n definition,\n resource,\n 'Generated API resources require at least one operation.',\n 'operations',\n ),\n );\n }\n\n const primaryKey = resolveGeneratedApiPrimaryKey(resource.collection);\n if (\n primaryKey === undefined &&\n resource.operations.some((operation) => operation !== 'list' && operation !== 'create')\n ) {\n diagnostics.push(\n resourceDiagnostic(\n definition,\n resource,\n 'Generated API resources need a primaryKey field for read/update/delete operations.',\n 'collection.primaryKey',\n ),\n );\n }\n}\n\nfunction resourceDiagnostic(\n definition: GeneratedApiDefinition,\n resource: GeneratedApiResourceDefinition,\n message: string,\n path: string,\n): DataSourceDiagnostic {\n return {\n code: 'invalid-config',\n dataSourceId: definition.id,\n endpointId: resource.id,\n message,\n path: `resources.${resource.id}.${path}`,\n severity: 'error',\n };\n}\n\nfunction createGeneratedApiSourceMetadata(definition: GeneratedApiDefinition): DataContractValue {\n return {\n ...readMetadataRecord(definition.metadata),\n basePath: definition.basePath,\n source: 'generated-api',\n auth: createGeneratedApiAuthMetadata(definition.auth),\n };\n}\n\nfunction createGeneratedApiResourceMetadata(\n definition: GeneratedApiDefinition,\n resource: GeneratedApiResourceDefinition,\n): DataContractValue {\n return {\n ...readMetadataRecord(resource.metadata),\n adapterId: definition.database.id,\n collection: resource.collection.name,\n schema: resource.collection.schema ?? null,\n source: 'generated-api',\n seed: resource.seed ?? [],\n policies: createGeneratedApiPolicyMetadata(resource.policies),\n };\n}\n\nfunction createGeneratedApiOperationMetadata(\n definition: GeneratedApiDefinition,\n resource: GeneratedApiResourceDefinition,\n operation: GeneratedApiCrudOperation,\n): DataContractValue {\n return {\n adapterId: definition.database.id,\n collection: resource.collection.name,\n operation,\n schema: resource.collection.schema ?? null,\n source: 'generated-api',\n policies: createGeneratedApiPolicyMetadata(\n resource.policies?.filter(\n (policy) => policy.operation === undefined || policy.operation === operation,\n ),\n ),\n };\n}\n\nfunction createGeneratedApiAuthMetadata(\n auth: GeneratedApiAuthRequirement | undefined,\n): DataContractValue {\n if (auth === undefined) return null;\n\n const metadata: Record<string, DataContractValue> = {};\n if (auth.required !== undefined) metadata.required = auth.required;\n if (auth.roles !== undefined) metadata.roles = auth.roles;\n if (auth.permissions !== undefined) metadata.permissions = auth.permissions;\n if (auth.policy !== undefined) metadata.policy = auth.policy;\n if (auth.metadata !== undefined) metadata.metadata = auth.metadata;\n return metadata;\n}\n\nfunction createGeneratedApiPolicyMetadata(\n policies: readonly GeneratedApiOperationPolicyRef[] | undefined,\n): readonly DataContractValue[] {\n return (policies ?? []).map((policy) => {\n const metadata: Record<string, DataContractValue> = { id: policy.id };\n if (policy.operation !== undefined) metadata.operation = policy.operation;\n return metadata;\n });\n}\n\nfunction createGeneratedApiOperationName(\n resource: GeneratedApiResourceDefinition,\n operation: GeneratedApiCrudOperation,\n): string {\n return `${operation} ${resource.name ?? resource.id}`;\n}\n\nfunction mapGeneratedApiOperationIntent(operation: GeneratedApiCrudOperation): DataOperationIntent {\n if (operation === 'list' || operation === 'read') return 'read';\n if (operation === 'create') return 'create';\n if (operation === 'update') return 'update';\n return 'delete';\n}\n\nfunction createGeneratedApiOperationRequest(\n collection: DbCollectionDefinition,\n operation: GeneratedApiCrudOperation,\n primaryKeyParameter: DataOperationParameter | undefined,\n): DataOperationConfig['request'] {\n if (operation === 'list') {\n return {\n parameters: [\n { name: 'limit', location: 'query', schema: { type: 'integer' } },\n { name: 'offset', location: 'query', schema: { type: 'integer' } },\n ],\n };\n }\n\n if (operation === 'read' || operation === 'delete') {\n return primaryKeyParameter === undefined ? undefined : { parameters: [primaryKeyParameter] };\n }\n\n const parameters =\n operation === 'update' && primaryKeyParameter !== undefined ? [primaryKeyParameter] : undefined;\n\n return {\n parameters,\n schema: createGeneratedApiResourceSchema(collection),\n };\n}\n\nfunction createGeneratedApiOperationResponse(\n collection: DbCollectionDefinition,\n operation: GeneratedApiCrudOperation,\n): DataOperationConfig['response'] {\n const schema = createGeneratedApiResourceSchema(collection);\n\n if (operation === 'list') return { schema: { type: 'array', items: schema } };\n if (operation === 'delete') {\n return { schema: { type: 'object', properties: { deleted: { type: 'boolean' } } } };\n }\n return { schema };\n}\n\nfunction createPrimaryKeyParameter(primaryKey: string): DataOperationParameter {\n return {\n name: primaryKey,\n location: 'path',\n required: true,\n schema: { type: 'string' },\n };\n}\n\nfunction resolveGeneratedApiPrimaryKey(collection: DbCollectionDefinition): string | undefined {\n return collection.primaryKey ?? collection.fields.find((field) => field.unique === true)?.name;\n}\n\nfunction createGeneratedApiFieldSchema(field: DbFieldDefinition): DataSchema {\n return {\n ...mapDbFieldTypeToDataSchema(field.type),\n default: field.defaultValue,\n };\n}\n\nfunction mapDbFieldTypeToDataSchema(type: DbFieldType): DataSchema {\n if (type === 'boolean') return { type: 'boolean' };\n if (type === 'datetime') return { type: 'string', format: 'date-time' };\n if (type === 'json') return { type: 'object' };\n if (type === 'number') return { type: 'number' };\n if (type === 'uuid') return { type: 'string', format: 'uuid' };\n return { type: 'string' };\n}\n\nfunction readMetadataRecord(\n value: DataContractValue | undefined,\n): Record<string, DataContractValue> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return {};\n const metadata: Record<string, DataContractValue> = {};\n for (const [key, item] of Object.entries(value)) metadata[key] = item;\n return metadata;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { CredentialRef, DataContractValue, DataSchema, DataSchemaRegistry, DataSourceDiagnostic, DataSourceDiagnosticResult, GraphQlDataSourceConfig, OperationId } from '@ankhorage/contracts/data';
1
+ import type { CredentialRef, DataContractValue, DataSchema, DataSchemaRegistry, DataSourceDiagnostic, DataSourceDiagnosticResult, ExternalGraphQlApiDataSourceConfig, OperationId } from '@ankhorage/contracts/data';
2
2
  export declare const GRAPHQL_INTROSPECTION_QUERY = "query AnkhorageGraphQlIntrospection {\n __schema {\n queryType { name }\n mutationType { name }\n subscriptionType { name }\n types {\n kind\n name\n description\n fields {\n name\n description\n args {\n name\n description\n type { kind name ofType { kind name ofType { kind name ofType { kind name } } } }\n defaultValue\n }\n type { kind name ofType { kind name ofType { kind name ofType { kind name } } } }\n }\n inputFields {\n name\n description\n type { kind name ofType { kind name ofType { kind name ofType { kind name } } } }\n defaultValue\n }\n enumValues { name description }\n possibleTypes { kind name }\n }\n }\n}";
3
3
  export type GraphQlOperationKind = 'mutation' | 'query' | 'subscription';
4
4
  export interface GraphQlIntrospectionTypeRef {
@@ -75,9 +75,9 @@ export interface GraphQlIntrospectionRequest {
75
75
  }
76
76
  export declare function createGraphQlIntrospectionRequest(): GraphQlIntrospectionRequest;
77
77
  export declare function normalizeGraphQlOperationId(kind: GraphQlOperationKind, name: string): OperationId;
78
- export declare function createGraphQlDataSource(definition: GraphQlDataSourceDefinition): DataSourceDiagnosticResult<GraphQlDataSourceConfig>;
78
+ export declare function createGraphQlDataSource(definition: GraphQlDataSourceDefinition): DataSourceDiagnosticResult<ExternalGraphQlApiDataSourceConfig>;
79
79
  export declare function validateGraphQlDataSource(definition: GraphQlDataSourceDefinition): readonly DataSourceDiagnostic[];
80
- export declare function normalizeGraphQlDataSource(definition: GraphQlDataSourceDefinition): GraphQlDataSourceConfig;
80
+ export declare function normalizeGraphQlDataSource(definition: GraphQlDataSourceDefinition): ExternalGraphQlApiDataSourceConfig;
81
81
  export declare function normalizeGraphQlIntrospectionSchemas(introspection: GraphQlIntrospectionResult | undefined): DataSchemaRegistry | undefined;
82
82
  export declare function normalizeGraphQlIntrospectionOperations(introspection: GraphQlIntrospectionResult | undefined): readonly GraphQlOperationDefinition[];
83
83
  //# sourceMappingURL=index.d.ts.map