@loxtep/sdk 0.7.22 → 0.7.24

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 (73) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +6 -1
  3. package/dist/cli/commands/connectors-cmd.d.ts +23 -0
  4. package/dist/cli/commands/connectors-cmd.d.ts.map +1 -0
  5. package/dist/cli/commands/connectors-cmd.js +32 -0
  6. package/dist/cli/commands/connectors-cmd.js.map +1 -0
  7. package/dist/cli/commands/deploy-cmd.d.ts +5 -1
  8. package/dist/cli/commands/deploy-cmd.d.ts.map +1 -1
  9. package/dist/cli/commands/deploy-cmd.js +25 -2
  10. package/dist/cli/commands/deploy-cmd.js.map +1 -1
  11. package/dist/cli/commands/ingest-cmd.d.ts +7 -2
  12. package/dist/cli/commands/ingest-cmd.d.ts.map +1 -1
  13. package/dist/cli/commands/ingest-cmd.js +119 -29
  14. package/dist/cli/commands/ingest-cmd.js.map +1 -1
  15. package/dist/cli/commands/lint-cmd.d.ts +17 -0
  16. package/dist/cli/commands/lint-cmd.d.ts.map +1 -0
  17. package/dist/cli/commands/lint-cmd.js +61 -0
  18. package/dist/cli/commands/lint-cmd.js.map +1 -0
  19. package/dist/cli/help.d.ts.map +1 -1
  20. package/dist/cli/help.js +11 -3
  21. package/dist/cli/help.js.map +1 -1
  22. package/dist/cli/index.js +24 -3
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/client/connectors.d.ts.map +1 -1
  25. package/dist/client/connectors.js +7 -1
  26. package/dist/client/connectors.js.map +1 -1
  27. package/dist/errors/parse-http.d.ts.map +1 -1
  28. package/dist/errors/parse-http.js +33 -10
  29. package/dist/errors/parse-http.js.map +1 -1
  30. package/dist/index.d.ts +5 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +3 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/lib/entity-json-schemas/ajv-loader.cjs +17 -0
  35. package/dist/lib/entity-json-schemas/index.d.ts +5 -0
  36. package/dist/lib/entity-json-schemas/index.d.ts.map +1 -0
  37. package/dist/lib/entity-json-schemas/index.js +3 -0
  38. package/dist/lib/entity-json-schemas/index.js.map +1 -0
  39. package/dist/lib/entity-json-schemas/types.d.ts +21 -0
  40. package/dist/lib/entity-json-schemas/types.d.ts.map +1 -0
  41. package/dist/lib/entity-json-schemas/types.js +32 -0
  42. package/dist/lib/entity-json-schemas/types.js.map +1 -0
  43. package/dist/lib/entity-json-schemas/validate-entity.d.ts +39 -0
  44. package/dist/lib/entity-json-schemas/validate-entity.d.ts.map +1 -0
  45. package/dist/lib/entity-json-schemas/validate-entity.js +143 -0
  46. package/dist/lib/entity-json-schemas/validate-entity.js.map +1 -0
  47. package/dist/lib/sdk-ingest-bundle.d.ts +60 -4
  48. package/dist/lib/sdk-ingest-bundle.d.ts.map +1 -1
  49. package/dist/lib/sdk-ingest-bundle.js +144 -51
  50. package/dist/lib/sdk-ingest-bundle.js.map +1 -1
  51. package/dist/lib/workspace-lint.d.ts +28 -0
  52. package/dist/lib/workspace-lint.d.ts.map +1 -0
  53. package/dist/lib/workspace-lint.js +199 -0
  54. package/dist/lib/workspace-lint.js.map +1 -0
  55. package/docs/getting-started.md +2 -0
  56. package/docs/quick-reference.md +1 -0
  57. package/docs/sdk-first-ingest.md +19 -3
  58. package/package.json +6 -2
  59. package/schemas/entity-json-schemas/SCHEMA_SYNC.md +18 -0
  60. package/schemas/entity-json-schemas/connection.json +310 -0
  61. package/schemas/entity-json-schemas/connector.json +188 -0
  62. package/schemas/entity-json-schemas/container.json +71 -0
  63. package/schemas/entity-json-schemas/contract.json +228 -0
  64. package/schemas/entity-json-schemas/data-product.json +163 -0
  65. package/schemas/entity-json-schemas/domain.json +74 -0
  66. package/schemas/entity-json-schemas/export.json +126 -0
  67. package/schemas/entity-json-schemas/odps-product.json +150 -0
  68. package/schemas/entity-json-schemas/quality-rule.json +115 -0
  69. package/schemas/entity-json-schemas/schema.json +206 -0
  70. package/schemas/entity-json-schemas/transformation.json +166 -0
  71. package/schemas/entity-json-schemas/validation.json +152 -0
  72. package/schemas/entity-json-schemas/workflow-graph.json +116 -0
  73. package/schemas/entity-json-schemas/workflow.json +193 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Offline lint of a local Loxtep project package (entity schemas + relationships).
3
+ */
4
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
5
+ import { join, relative } from 'node:path';
6
+ import { EntityType, validateEntity } from './entity-json-schemas/index.js';
7
+ function readJsonObject(filePath) {
8
+ try {
9
+ const raw = JSON.parse(readFileSync(filePath, 'utf8'));
10
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
11
+ return raw;
12
+ }
13
+ return null;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ function listJsonFiles(dir) {
20
+ if (!existsSync(dir))
21
+ return [];
22
+ const out = [];
23
+ for (const name of readdirSync(dir)) {
24
+ const full = join(dir, name);
25
+ let st;
26
+ try {
27
+ st = statSync(full);
28
+ }
29
+ catch {
30
+ continue;
31
+ }
32
+ if (st.isDirectory()) {
33
+ out.push(...listJsonFiles(full));
34
+ }
35
+ else if (name.endsWith('.json')) {
36
+ out.push(full);
37
+ }
38
+ }
39
+ return out;
40
+ }
41
+ function classifyEntity(projectDir, filePath) {
42
+ const rel = relative(projectDir, filePath).replace(/\\/g, '/');
43
+ if (rel.startsWith('connectors/') && rel.endsWith('.json')) {
44
+ return EntityType.CONNECTOR;
45
+ }
46
+ if (rel.match(/^workflows\/[^/]+\/workflow\.json$/)) {
47
+ return EntityType.WORKFLOW;
48
+ }
49
+ if (rel.match(/^workflows\/[^/]+\/connections\/[^/]+\.json$/)) {
50
+ return EntityType.CONNECTION;
51
+ }
52
+ if (rel.match(/^workflows\/[^/]+\/data-products\/[^/]+\.json$/)) {
53
+ return EntityType.DATA_PRODUCT;
54
+ }
55
+ if (rel.match(/^workflows\/[^/]+\/transformations\/[^/]+\.json$/)) {
56
+ return EntityType.TRANSFORMATION;
57
+ }
58
+ if (rel.match(/^workflows\/[^/]+\/validations\/[^/]+\.json$/)) {
59
+ return EntityType.VALIDATION;
60
+ }
61
+ if (rel.startsWith('domains/') && rel.endsWith('.json')) {
62
+ return EntityType.DOMAIN;
63
+ }
64
+ return null;
65
+ }
66
+ function discoverEntities(projectDir, workflowId) {
67
+ const entities = [];
68
+ const roots = [join(projectDir, 'connectors'), join(projectDir, 'domains')];
69
+ if (workflowId) {
70
+ roots.push(join(projectDir, 'workflows', workflowId));
71
+ }
72
+ else {
73
+ roots.push(join(projectDir, 'workflows'));
74
+ }
75
+ for (const root of roots) {
76
+ for (const filePath of listJsonFiles(root)) {
77
+ const entityType = classifyEntity(projectDir, filePath);
78
+ if (!entityType)
79
+ continue;
80
+ const data = readJsonObject(filePath);
81
+ const rel = relative(projectDir, filePath).replace(/\\/g, '/');
82
+ if (!data) {
83
+ entities.push({
84
+ path: rel,
85
+ entityType,
86
+ data: {},
87
+ });
88
+ // mark unreadable via empty + special handling below
89
+ continue;
90
+ }
91
+ entities.push({ path: rel, entityType, data });
92
+ }
93
+ }
94
+ return entities;
95
+ }
96
+ /**
97
+ * Lint local entity JSON against shipped schemas and basic relationship checks.
98
+ */
99
+ export function lintLocalPackage(options) {
100
+ const { projectDir, workflow_id } = options;
101
+ const issues = [];
102
+ if (!existsSync(projectDir)) {
103
+ return {
104
+ ok: false,
105
+ files_checked: 0,
106
+ issues: [{ path: projectDir, severity: 'error', message: 'Project directory not found' }],
107
+ };
108
+ }
109
+ const entities = discoverEntities(projectDir, workflow_id);
110
+ const byId = new Map();
111
+ for (const entity of entities) {
112
+ if (Object.keys(entity.data).length === 0) {
113
+ issues.push({
114
+ path: entity.path,
115
+ severity: 'error',
116
+ message: 'Invalid or unreadable JSON object',
117
+ });
118
+ continue;
119
+ }
120
+ const result = validateEntity(entity.entityType, entity.data);
121
+ if (!result.valid && result.errors) {
122
+ for (const err of result.errors) {
123
+ issues.push({
124
+ path: `${entity.path}${err.path}`,
125
+ severity: 'error',
126
+ message: err.message,
127
+ });
128
+ }
129
+ }
130
+ const idKey = entity.data.connector_id ||
131
+ entity.data.connection_id ||
132
+ entity.data.workflow_id ||
133
+ entity.data.data_product_id ||
134
+ entity.data.domain_id;
135
+ if (idKey) {
136
+ byId.set(idKey, entity);
137
+ }
138
+ }
139
+ // Relationship checks
140
+ for (const entity of entities) {
141
+ if (entity.entityType === EntityType.CONNECTION) {
142
+ const connectorId = entity.data.connector_id;
143
+ if (typeof connectorId === 'string' && connectorId.length > 0) {
144
+ // connector may exist only remotely; warn only if local connectors/ is present
145
+ // and does not contain this id — keep as error when local connector file expected.
146
+ const localConnectors = entities.filter(e => e.entityType === EntityType.CONNECTOR);
147
+ if (localConnectors.length > 0 &&
148
+ !localConnectors.some(c => c.data.connector_id === connectorId)) {
149
+ issues.push({
150
+ path: entity.path,
151
+ severity: 'error',
152
+ message: `connector_id "${connectorId}" not found under connectors/`,
153
+ });
154
+ }
155
+ }
156
+ else {
157
+ issues.push({
158
+ path: entity.path,
159
+ severity: 'error',
160
+ message: 'connection is missing connector_id',
161
+ });
162
+ }
163
+ }
164
+ if (entity.entityType === EntityType.DATA_PRODUCT) {
165
+ const upstream = entity.data.upstream_entity_id;
166
+ if (typeof upstream === 'string' && upstream.length > 0 && !byId.has(upstream)) {
167
+ // Upstream may be a connection in the same workflow — check connection ids
168
+ const conn = entities.find(e => e.entityType === EntityType.CONNECTION && e.data.connection_id === upstream);
169
+ if (!conn) {
170
+ issues.push({
171
+ path: entity.path,
172
+ severity: 'error',
173
+ message: `upstream_entity_id "${upstream}" not found in local package`,
174
+ });
175
+ }
176
+ }
177
+ }
178
+ }
179
+ return {
180
+ ok: issues.length === 0,
181
+ issues,
182
+ files_checked: entities.length,
183
+ };
184
+ }
185
+ /**
186
+ * True when the project has at least one entity JSON package file to lint.
187
+ */
188
+ export function hasLocalEntityPackage(projectDir) {
189
+ const workflowsDir = join(projectDir, 'workflows');
190
+ if (!existsSync(workflowsDir))
191
+ return false;
192
+ for (const name of readdirSync(workflowsDir)) {
193
+ const wfJson = join(workflowsDir, name, 'workflow.json');
194
+ if (existsSync(wfJson))
195
+ return true;
196
+ }
197
+ return existsSync(join(projectDir, 'connectors')) && listJsonFiles(join(projectDir, 'connectors')).length > 0;
198
+ }
199
+ //# sourceMappingURL=workspace-lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-lint.js","sourceRoot":"","sources":["../../src/lib/workspace-lint.ts"],"names":[],"mappings":"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"}
@@ -170,6 +170,8 @@ from your application:
170
170
 
171
171
  ```bash
172
172
  pnpm exec loxtep ingest provision --name app-events
173
+ pnpm exec loxtep lint
174
+ pnpm exec loxtep deploy
173
175
  pnpm exec loxtep data-products list
174
176
  ```
175
177
 
@@ -193,6 +193,7 @@ pnpm exec loxtep generate
193
193
 
194
194
  ```bash
195
195
  pnpm exec loxtep ingest provision --name app-events
196
+ pnpm exec loxtep lint && pnpm exec loxtep deploy
196
197
  ```
197
198
 
198
199
  See [Write to a data product](./sdk-first-ingest.md).
@@ -22,20 +22,34 @@ You should see `project_id` and `instance_id` from `attach`.
22
22
 
23
23
  ---
24
24
 
25
- ## Phase 2 — Create your data product
25
+ ## Phase 2 — Create your data product (local package)
26
26
 
27
27
  Pick a name your app will use (example: `app-events`):
28
28
 
29
29
  ```bash
30
+ pnpm exec loxtep connectors list --type sdk # optional: see existing SDK connectors
30
31
  pnpm exec loxtep ingest provision --name app-events
32
+ pnpm exec loxtep lint
31
33
  ```
32
34
 
33
- That is all you need for SDK writes one command, one name.
35
+ `ingest provision` reuses an existing SDK connector when one exists (or creates one),
36
+ then writes a **local** workflow package under `connectors/` and `workflows/<id>/`.
37
+ It does **not** deploy by default. Validate with `loxtep lint`, then publish:
38
+
39
+ ```bash
40
+ pnpm exec loxtep deploy --dry-run # lint only
41
+ pnpm exec loxtep deploy # lint + compile/deploy modules
42
+ # or: pnpm exec loxtep ingest provision --name app-events --deploy
43
+ ```
44
+
45
+ That local package is what you iterate on; deploy pushes it to your instance.
34
46
 
35
47
  ---
36
48
 
37
49
  ## Phase 3 — Confirm it exists
38
50
 
51
+ After deploy:
52
+
39
53
  ```bash
40
54
  pnpm exec loxtep data-products list
41
55
  ```
@@ -90,6 +104,8 @@ pnpm exec loxtep login
90
104
  pnpm exec loxtep init
91
105
  pnpm exec loxtep attach --instance <instance-id>
92
106
  pnpm exec loxtep ingest provision --name app-events
107
+ pnpm exec loxtep lint
108
+ pnpm exec loxtep deploy
93
109
  pnpm exec loxtep data-products list
94
110
  ```
95
111
 
@@ -102,7 +118,7 @@ Then `get_writer('app-events')` in your app.
102
118
  | What you see | What to do |
103
119
  | ------------ | ---------- |
104
120
  | Data product not found | Run `data-products list`. Check the name matches `get_writer('…')` exactly. |
105
- | Not ready to accept writes | Run `ingest provision` again for that name. Make sure you ran `attach` first. |
121
+ | Not ready to accept writes | Run `ingest provision`, then `deploy` (or `ingest provision --deploy`). Make sure you ran `attach` first. |
106
122
  | Multiple products with the same name | Pass the data product id to `get_writer`, or ensure `attach` points at the right instance. |
107
123
  | No domain available | Create a domain in the Loxtep UI, or pass `--domain-id` to `ingest provision`. |
108
124
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loxtep/sdk",
3
- "version": "0.7.22",
3
+ "version": "0.7.24",
4
4
  "description": "Loxtep SDK for Node.js — the Enterprise Context Layer: data products, workflows, projects, queues, and machine-usable context for AI",
5
5
  "author": "Loxtep <engineering@loxtep.io>",
6
6
  "homepage": "https://loxtep.io",
@@ -33,13 +33,15 @@
33
33
  "files": [
34
34
  "dist",
35
35
  "docs",
36
+ "schemas",
36
37
  "CHANGELOG.md"
37
38
  ],
38
39
  "scripts": {
39
- "build": "tsc",
40
+ "build": "tsc && mkdir -p dist/lib/entity-json-schemas && cp src/lib/entity-json-schemas/ajv-loader.cjs dist/lib/entity-json-schemas/ajv-loader.cjs",
40
41
  "prepublishOnly": "npm run build",
41
42
  "test": "jest --config jest.config.cjs",
42
43
  "lint": "eslint src --ext .ts",
44
+ "sync:entity-schemas": "node scripts/sync-entity-schemas.mjs",
43
45
  "generate:api-types": "tsx scripts/generate-api-types.ts",
44
46
  "docs": "typedoc"
45
47
  },
@@ -82,6 +84,8 @@
82
84
  "@smithy/protocol-http": "^5.4.3",
83
85
  "@smithy/signature-v4": "^5.4.3",
84
86
  "@smithy/types": "^4.14.2",
87
+ "ajv": "^8.20.0",
88
+ "ajv-formats": "^3.0.1",
85
89
  "js-yaml": "^4.3.0",
86
90
  "leo-sdk": "7.1.21",
87
91
  "zod": "^4.4.3"
@@ -0,0 +1,18 @@
1
+ # Entity JSON schemas (vendored)
2
+
3
+ These JSON Schema draft-07 files are a copy of the Loxtep platform schemas used
4
+ for customer workspace entity validation.
5
+
6
+ **Source (canonical):**
7
+ `loxtep/platform-backend/_core/src/customer-workspace/entity-json-schemas/`
8
+
9
+ **Sync command** (from `nodejs/`):
10
+
11
+ ```bash
12
+ node scripts/sync-entity-schemas.mjs /path/to/loxtep
13
+ ```
14
+
15
+ When platform schemas change, re-run the sync script and note the date/source
16
+ commit in the SDK CHANGELOG.
17
+
18
+ **Last synced:** 2026-07-24 (from local loxtep checkout)
@@ -0,0 +1,310 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://loxtep.io/schemas/entity/connection.json",
4
+ "title": "Connection Entity",
5
+ "description": "JSON Schema for Connection entity in customer workspace. Connections are configured instances of connectors with credentials and settings.",
6
+ "type": "object",
7
+ "required": [
8
+ "connection_id",
9
+ "organization_id",
10
+ "project_id",
11
+ "key",
12
+ "name",
13
+ "type",
14
+ "status"
15
+ ],
16
+ "properties": {
17
+ "connection_id": {
18
+ "type": "string",
19
+ "format": "uuid",
20
+ "description": "Unique identifier for the connection"
21
+ },
22
+ "organization_id": {
23
+ "type": "string",
24
+ "format": "uuid",
25
+ "description": "Organization that owns this connection"
26
+ },
27
+ "project_id": {
28
+ "type": "string",
29
+ "format": "uuid",
30
+ "description": "Project that contains this connection"
31
+ },
32
+ "workflow_id": {
33
+ "type": ["string", "null"],
34
+ "format": "uuid",
35
+ "description": "Workflow this connection belongs to; null for project-level/draft connections"
36
+ },
37
+ "connector_id": {
38
+ "type": ["string", "null"],
39
+ "format": "uuid",
40
+ "description": "Reference to the connector definition this connection uses; null for project-level/draft connections"
41
+ },
42
+ "sync_entity": {
43
+ "type": ["string", "null"],
44
+ "pattern": "^[a-z0-9-_]+$",
45
+ "description": "For API poll connectors: which entity (e.g. orders, customers) this connection syncs. Must match connector supported_entities when present."
46
+ },
47
+ "key": {
48
+ "type": "string",
49
+ "minLength": 1,
50
+ "maxLength": 255,
51
+ "pattern": "^[a-z0-9-_]+$",
52
+ "description": "Unique key for the connection within the organization"
53
+ },
54
+ "name": {
55
+ "type": "string",
56
+ "minLength": 1,
57
+ "maxLength": 255,
58
+ "description": "Display name for the connection"
59
+ },
60
+ "description": {
61
+ "type": ["string", "null"],
62
+ "maxLength": 1000,
63
+ "description": "Connection description"
64
+ },
65
+ "type": {
66
+ "type": "string",
67
+ "minLength": 1,
68
+ "description": "Connection type: database, api, webhook, file, or connector type (e.g. spotify, shopify)"
69
+ },
70
+ "status": {
71
+ "type": "string",
72
+ "enum": ["active", "inactive", "error"],
73
+ "description": "Connection status"
74
+ },
75
+ "credential_fields": {
76
+ "type": "object",
77
+ "description": "Encrypted credential fields for the connection (structure depends on connector auth_type)",
78
+ "properties": {
79
+ "api_key": {
80
+ "type": ["string", "null"],
81
+ "description": "API key (encrypted)"
82
+ },
83
+ "api_secret": {
84
+ "type": ["string", "null"],
85
+ "description": "API secret (encrypted)"
86
+ },
87
+ "oauth_access_token": {
88
+ "type": ["string", "null"],
89
+ "description": "OAuth access token (encrypted)"
90
+ },
91
+ "oauth_refresh_token": {
92
+ "type": ["string", "null"],
93
+ "description": "OAuth refresh token (encrypted)"
94
+ },
95
+ "oauth_expires_at": {
96
+ "type": ["string", "null"],
97
+ "format": "date-time",
98
+ "description": "OAuth token expiration time"
99
+ },
100
+ "username": {
101
+ "type": ["string", "null"],
102
+ "description": "Username for basic auth (encrypted)"
103
+ },
104
+ "password": {
105
+ "type": ["string", "null"],
106
+ "description": "Password for basic auth (encrypted)"
107
+ },
108
+ "bearer_token": {
109
+ "type": ["string", "null"],
110
+ "description": "Bearer token (encrypted)"
111
+ },
112
+ "custom_credentials": {
113
+ "type": ["object", "null"],
114
+ "additionalProperties": {
115
+ "type": "string"
116
+ },
117
+ "description": "Custom credential fields (encrypted)"
118
+ }
119
+ },
120
+ "additionalProperties": false
121
+ },
122
+ "credential_parameter_store_refs": {
123
+ "type": "object",
124
+ "description": "SSM Parameter Store paths for OAuth/credentials (keyed by credential name)",
125
+ "additionalProperties": {
126
+ "type": "string"
127
+ }
128
+ },
129
+ "configuration": {
130
+ "type": "object",
131
+ "additionalProperties": true,
132
+ "default": {},
133
+ "description": "Connection configuration (non-sensitive settings)"
134
+ },
135
+ "metadata": {
136
+ "type": "object",
137
+ "additionalProperties": true,
138
+ "default": {},
139
+ "description": "Additional metadata for the connection"
140
+ },
141
+ "verified": {
142
+ "type": "boolean",
143
+ "default": false,
144
+ "description": "Whether the connection has been verified/tested"
145
+ },
146
+ "draft": {
147
+ "type": "boolean",
148
+ "default": true,
149
+ "description": "Whether this connection is still in draft state"
150
+ },
151
+ "last_tested": {
152
+ "type": ["string", "null"],
153
+ "format": "date-time",
154
+ "description": "ISO 8601 timestamp when connection was last tested"
155
+ },
156
+ "created_by": {
157
+ "type": ["string", "null"],
158
+ "format": "uuid",
159
+ "description": "User ID who created the connection"
160
+ },
161
+ "updated_by": {
162
+ "type": ["string", "null"],
163
+ "format": "uuid",
164
+ "description": "User ID who last updated the connection"
165
+ },
166
+ "created_at": {
167
+ "type": "string",
168
+ "format": "date-time",
169
+ "description": "ISO 8601 timestamp when connection was created"
170
+ },
171
+ "updated_at": {
172
+ "type": "string",
173
+ "format": "date-time",
174
+ "description": "ISO 8601 timestamp when connection was last updated"
175
+ },
176
+ "deleted_at": {
177
+ "type": ["string", "null"],
178
+ "format": "date-time",
179
+ "description": "ISO 8601 timestamp when connection was deleted (soft delete)"
180
+ },
181
+ "ingestion_config": {
182
+ "type": ["object", "null"],
183
+ "description": "Structured ingestion settings (schedule, batch, polling). Extensible per connection type.",
184
+ "properties": {
185
+ "schedule": {
186
+ "type": ["string", "null"],
187
+ "description": "Cron expression (LeoCron `time`) or platform-supported schedule string for this connection's registered bot. Distinct from `polling_interval_ms`, which is a millisecond gap for application-level throttling between requests—not the Leo schedule field."
188
+ },
189
+ "runtime_memory_mb": {
190
+ "description": "Shared runtimes tier for trigger registration (256–2048). Defaults to 256 when unset. LeoCron `lambdaName`: `w-{org4}-{inst4}-bot-connectors-{mb}-rtime`; bot id: `w-{org4}-{inst4}-{workflow4}-bot-connectors-{mb}-{container_id}`. Instance `connection_details.runtimes_stack_identifier` may override the org/instance middle segment (CDK `microserviceIdentifier` = `w-{that middle}`).",
191
+ "oneOf": [
192
+ { "type": "null" },
193
+ { "type": "integer", "enum": [256, 512, 1024, 2048] }
194
+ ]
195
+ },
196
+ "fetch_action_key": {
197
+ "type": ["string", "null"],
198
+ "description": "Declarative connector package action key used for polling fetches (e.g. shopify_get_orders). Often matches sync_plan entity fetch_action_key."
199
+ },
200
+ "batch_size": {
201
+ "type": ["integer", "null"],
202
+ "description": "Batch size for polling (default 100)"
203
+ },
204
+ "polling_interval_ms": {
205
+ "type": ["integer", "null"],
206
+ "description": "Polling interval in milliseconds"
207
+ },
208
+ "max_retries": {
209
+ "type": ["integer", "null"],
210
+ "description": "Max retries on failure"
211
+ },
212
+ "timeout_ms": {
213
+ "type": ["integer", "null"],
214
+ "description": "Request/timeout in milliseconds"
215
+ }
216
+ },
217
+ "additionalProperties": true
218
+ },
219
+ "runtime_status": {
220
+ "type": ["string", "null"],
221
+ "enum": ["not_deployed", "deploying", "running", "paused", "error", null],
222
+ "description": "Deployment state of the connection's bot (written at deploy time)"
223
+ },
224
+ "auth_type": {
225
+ "type": ["string", "null"],
226
+ "enum": [
227
+ "oauth2",
228
+ "api_key",
229
+ "webhook",
230
+ "data_product_rbac",
231
+ "none",
232
+ null
233
+ ],
234
+ "description": "Authentication type for the connection (optional, backwards compat)."
235
+ },
236
+ "ingestion_type": {
237
+ "type": ["string", "null"],
238
+ "enum": [
239
+ "polling",
240
+ "webhook",
241
+ "data_product",
242
+ "database_cdc",
243
+ "file",
244
+ "api_sdk",
245
+ "stream",
246
+ "other",
247
+ null
248
+ ],
249
+ "description": "How data is ingested (optional)."
250
+ },
251
+ "export_type": {
252
+ "type": ["string", "null"],
253
+ "enum": ["file", "database", "stream", "api_sdk", "other", null],
254
+ "description": "How data is exported (optional)."
255
+ },
256
+ "source_schema": {
257
+ "type": ["object", "null"],
258
+ "description": "Source schema reference (optional)."
259
+ },
260
+ "destination_schema": {
261
+ "type": ["object", "null"],
262
+ "description": "Destination schema reference (optional)."
263
+ },
264
+ "retry_policy": {
265
+ "type": ["object", "null"],
266
+ "description": "Retry policy (max_retries, backoff, etc.).",
267
+ "properties": {
268
+ "max_retries": { "type": ["integer", "null"] },
269
+ "backoff_ms": { "type": ["integer", "null"] }
270
+ },
271
+ "additionalProperties": true
272
+ },
273
+ "timeout": {
274
+ "type": ["integer", "null"],
275
+ "description": "Timeout in milliseconds (optional)."
276
+ },
277
+ "concurrency": {
278
+ "type": ["integer", "null"],
279
+ "minimum": 1,
280
+ "description": "Concurrency limit (optional)."
281
+ },
282
+ "batch_size": {
283
+ "type": ["integer", "null"],
284
+ "minimum": 1,
285
+ "description": "Batch size for sync (optional)."
286
+ },
287
+ "connector_mode": {
288
+ "type": ["string", "null"],
289
+ "enum": ["inbound", "outbound", "bidirectional", null],
290
+ "description": "For enrichment workflows: mid-flow connection must be 'bidirectional'. For ingestion head use 'inbound'; for consumption tail use 'outbound'. Must match connector supported_directions."
291
+ }
292
+ },
293
+ "upstream_entity_id": {
294
+ "type": ["string", "null"],
295
+ "format": "uuid",
296
+ "description": "Upstream entity in the flow graph (e.g. data-product in consumption flows). Null for source connections."
297
+ },
298
+ "upstream_entity_type": {
299
+ "type": ["string", "null"],
300
+ "enum": [
301
+ "connections",
302
+ "transformations",
303
+ "validations",
304
+ "data-products",
305
+ null
306
+ ],
307
+ "description": "Entity type of the upstream entity."
308
+ },
309
+ "additionalProperties": false
310
+ }