@loxtep/sdk 0.7.21 → 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.
- package/CHANGELOG.md +42 -0
- package/README.md +17 -13
- package/dist/cli/commands/config-cmd.js +1 -1
- package/dist/cli/commands/connectors-cmd.d.ts +23 -0
- package/dist/cli/commands/connectors-cmd.d.ts.map +1 -0
- package/dist/cli/commands/connectors-cmd.js +32 -0
- package/dist/cli/commands/connectors-cmd.js.map +1 -0
- package/dist/cli/commands/deploy-cmd.d.ts +5 -1
- package/dist/cli/commands/deploy-cmd.d.ts.map +1 -1
- package/dist/cli/commands/deploy-cmd.js +25 -2
- package/dist/cli/commands/deploy-cmd.js.map +1 -1
- package/dist/cli/commands/ingest-cmd.d.ts +7 -2
- package/dist/cli/commands/ingest-cmd.d.ts.map +1 -1
- package/dist/cli/commands/ingest-cmd.js +120 -31
- package/dist/cli/commands/ingest-cmd.js.map +1 -1
- package/dist/cli/commands/lint-cmd.d.ts +17 -0
- package/dist/cli/commands/lint-cmd.d.ts.map +1 -0
- package/dist/cli/commands/lint-cmd.js +61 -0
- package/dist/cli/commands/lint-cmd.js.map +1 -0
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +11 -6
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/index.js +24 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/client/connectors.d.ts.map +1 -1
- package/dist/client/connectors.js +7 -1
- package/dist/client/connectors.js.map +1 -1
- package/dist/errors/parse-http.d.ts.map +1 -1
- package/dist/errors/parse-http.js +33 -10
- package/dist/errors/parse-http.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/entity-json-schemas/ajv-loader.cjs +17 -0
- package/dist/lib/entity-json-schemas/index.d.ts +5 -0
- package/dist/lib/entity-json-schemas/index.d.ts.map +1 -0
- package/dist/lib/entity-json-schemas/index.js +3 -0
- package/dist/lib/entity-json-schemas/index.js.map +1 -0
- package/dist/lib/entity-json-schemas/types.d.ts +21 -0
- package/dist/lib/entity-json-schemas/types.d.ts.map +1 -0
- package/dist/lib/entity-json-schemas/types.js +32 -0
- package/dist/lib/entity-json-schemas/types.js.map +1 -0
- package/dist/lib/entity-json-schemas/validate-entity.d.ts +39 -0
- package/dist/lib/entity-json-schemas/validate-entity.d.ts.map +1 -0
- package/dist/lib/entity-json-schemas/validate-entity.js +143 -0
- package/dist/lib/entity-json-schemas/validate-entity.js.map +1 -0
- package/dist/lib/sdk-ingest-bundle.d.ts +60 -4
- package/dist/lib/sdk-ingest-bundle.d.ts.map +1 -1
- package/dist/lib/sdk-ingest-bundle.js +144 -51
- package/dist/lib/sdk-ingest-bundle.js.map +1 -1
- package/dist/lib/workspace-lint.d.ts +28 -0
- package/dist/lib/workspace-lint.d.ts.map +1 -0
- package/dist/lib/workspace-lint.js +199 -0
- package/dist/lib/workspace-lint.js.map +1 -0
- package/docs/getting-started.md +6 -16
- package/docs/quick-reference.md +9 -9
- package/docs/sdk-first-ingest.md +64 -227
- package/package.json +6 -2
- package/schemas/entity-json-schemas/SCHEMA_SYNC.md +18 -0
- package/schemas/entity-json-schemas/connection.json +310 -0
- package/schemas/entity-json-schemas/connector.json +188 -0
- package/schemas/entity-json-schemas/container.json +71 -0
- package/schemas/entity-json-schemas/contract.json +228 -0
- package/schemas/entity-json-schemas/data-product.json +163 -0
- package/schemas/entity-json-schemas/domain.json +74 -0
- package/schemas/entity-json-schemas/export.json +126 -0
- package/schemas/entity-json-schemas/odps-product.json +150 -0
- package/schemas/entity-json-schemas/quality-rule.json +115 -0
- package/schemas/entity-json-schemas/schema.json +206 -0
- package/schemas/entity-json-schemas/transformation.json +166 -0
- package/schemas/entity-json-schemas/validation.json +152 -0
- package/schemas/entity-json-schemas/workflow-graph.json +116 -0
- package/schemas/entity-json-schemas/workflow.json +193 -0
- package/docs/examples/generate-ingest-bundle.mjs +0 -133
- package/docs/examples/write-events.mjs +0 -54
|
@@ -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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"}
|
package/docs/getting-started.md
CHANGED
|
@@ -163,29 +163,19 @@ products, connectors, domains, queues, workflows). Re-run after platform changes
|
|
|
163
163
|
|
|
164
164
|
---
|
|
165
165
|
|
|
166
|
-
## Step 6:
|
|
166
|
+
## Step 6: Create a data product and write from your app
|
|
167
167
|
|
|
168
|
-
After `attach`,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
`get_writer` needs **deployment metadata** (queues and bots created at deploy
|
|
172
|
-
time). Creating a catalog row alone is not enough.
|
|
173
|
-
|
|
174
|
-
**Recommended:** follow **[SDK-first ingest](./sdk-first-ingest.md)** end to end.
|
|
175
|
-
Short version:
|
|
168
|
+
After `attach`, create a data product on that instance, then call `get_writer`
|
|
169
|
+
from your application:
|
|
176
170
|
|
|
177
171
|
```bash
|
|
178
|
-
pnpm exec loxtep domains list
|
|
179
|
-
|
|
180
|
-
# One command: SDK connector + workflow bundle + deploy (no MCP)
|
|
181
172
|
pnpm exec loxtep ingest provision --name app-events
|
|
182
|
-
|
|
173
|
+
pnpm exec loxtep lint
|
|
174
|
+
pnpm exec loxtep deploy
|
|
183
175
|
pnpm exec loxtep data-products list
|
|
184
|
-
node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
185
176
|
```
|
|
186
177
|
|
|
187
|
-
|
|
188
|
-
streaming — it registers catalog metadata without runtime bindings.
|
|
178
|
+
See **[Write to a data product](./sdk-first-ingest.md)** for the full flow and code sample.
|
|
189
179
|
|
|
190
180
|
---
|
|
191
181
|
|
package/docs/quick-reference.md
CHANGED
|
@@ -54,8 +54,8 @@ bus config from deployment metadata.
|
|
|
54
54
|
```typescript
|
|
55
55
|
const writer = await client.get_writer('orders'); // deployed data product name
|
|
56
56
|
|
|
57
|
-
writer.write({
|
|
58
|
-
writer.write({
|
|
57
|
+
writer.write({ order_id: 'ord-1', status: 'placed' });
|
|
58
|
+
writer.write({ order_id: 'ord-2', status: 'shipped' });
|
|
59
59
|
|
|
60
60
|
await writer.close(); // flushes all buffered events
|
|
61
61
|
```
|
|
@@ -65,8 +65,8 @@ await writer.close(); // flushes all buffered events
|
|
|
65
65
|
```python
|
|
66
66
|
writer = await client.get_writer("my-data-product")
|
|
67
67
|
|
|
68
|
-
writer.write({"
|
|
69
|
-
writer.write({"
|
|
68
|
+
writer.write({"order_id": "ord-1", "status": "placed"})
|
|
69
|
+
writer.write({"order_id": "ord-2", "status": "shipped"})
|
|
70
70
|
|
|
71
71
|
await writer.close() # flushes all buffered events
|
|
72
72
|
```
|
|
@@ -189,15 +189,15 @@ pnpm exec loxtep attach --instance <instance-id>
|
|
|
189
189
|
pnpm exec loxtep generate
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
-
**
|
|
192
|
+
**Create a data product and write from your app** (after attach):
|
|
193
193
|
|
|
194
194
|
```bash
|
|
195
|
-
pnpm exec loxtep
|
|
196
|
-
|
|
197
|
-
pnpm exec loxtep workflows deploy --project-id <id> --instance-id <id>
|
|
198
|
-
node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
195
|
+
pnpm exec loxtep ingest provision --name app-events
|
|
196
|
+
pnpm exec loxtep lint && pnpm exec loxtep deploy
|
|
199
197
|
```
|
|
200
198
|
|
|
199
|
+
See [Write to a data product](./sdk-first-ingest.md).
|
|
200
|
+
|
|
201
201
|
Code-first workflow modules:
|
|
202
202
|
|
|
203
203
|
```bash
|
package/docs/sdk-first-ingest.md
CHANGED
|
@@ -1,263 +1,94 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Write to a data product from your app
|
|
2
2
|
|
|
3
|
-
You
|
|
4
|
-
**
|
|
3
|
+
You installed the SDK, initialized a workspace, and attached an instance. Next:
|
|
4
|
+
**create a data product**, open a writer, and send events from your code.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
first — you want your app (or ETL job) to push events into a **source** data
|
|
8
|
-
product on your attached instance.
|
|
9
|
-
|
|
10
|
-
> **Full CLI workspace setup:** [Getting Started](./getting-started.md) steps 1–4.
|
|
11
|
-
> **Code-first workflow modules:** [Code-first CLI](./code-first-cli.md).
|
|
6
|
+
> Already set up login, init, and attach? [Getting started](./getting-started.md).
|
|
12
7
|
|
|
13
8
|
---
|
|
14
9
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
```text
|
|
18
|
-
Your app ──get_writer──▶ rstreams queue ──▶ source data product (catalog)
|
|
19
|
-
▲ ▲
|
|
20
|
-
│ │
|
|
21
|
-
@loxtep/sdk provisioned at deploy time
|
|
22
|
-
fromWorkspace()
|
|
23
|
-
```
|
|
10
|
+
## Phase 1 — Workspace ready
|
|
24
11
|
|
|
25
|
-
|
|
26
|
-
**deployment metadata** (`deployment_bindings` on the data product). A catalog
|
|
27
|
-
row alone is not enough — the ingestion workflow must be **saved and deployed**
|
|
28
|
-
on your attached instance first.
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## Prerequisites
|
|
33
|
-
|
|
34
|
-
Run from your scaffolded workspace (directory with `.loxtep/project.json`):
|
|
12
|
+
Run commands from your project directory (the folder with `.loxtep/project.json`).
|
|
35
13
|
|
|
36
14
|
```bash
|
|
37
|
-
pnpm exec loxtep
|
|
38
|
-
pnpm exec loxtep
|
|
15
|
+
pnpm exec loxtep login
|
|
16
|
+
pnpm exec loxtep init
|
|
17
|
+
pnpm exec loxtep attach --instance <instance-id>
|
|
18
|
+
pnpm exec loxtep config list
|
|
39
19
|
```
|
|
40
20
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## Overview (four phases)
|
|
46
|
-
|
|
47
|
-
| Phase | What | How |
|
|
48
|
-
| ----- | ---- | --- |
|
|
49
|
-
| 1 | Workspace ready | `login` → `init` → `attach` (done) |
|
|
50
|
-
| 2 | Provision runtime | SDK connector + workflow bundle + deploy |
|
|
51
|
-
| 3 | Verify | `data-products list` / `data-products get` |
|
|
52
|
-
| 4 | Write events | `LoxtepClient.fromWorkspace()` + `get_writer` |
|
|
53
|
-
|
|
54
|
-
Phases 2–4 below.
|
|
21
|
+
You should see `project_id` and `instance_id` from `attach`.
|
|
55
22
|
|
|
56
23
|
---
|
|
57
24
|
|
|
58
|
-
## Phase 2 —
|
|
59
|
-
|
|
60
|
-
Runtime provisioning creates the workflow graph (SDK connection node → source
|
|
61
|
-
data product node), saves it to your project workspace, and **deploys** it to your
|
|
62
|
-
instance so queues and bots exist.
|
|
63
|
-
|
|
64
|
-
### Option A — CLI one-shot (recommended, no MCP)
|
|
25
|
+
## Phase 2 — Create your data product (local package)
|
|
65
26
|
|
|
66
|
-
|
|
27
|
+
Pick a name your app will use (example: `app-events`):
|
|
67
28
|
|
|
68
29
|
```bash
|
|
30
|
+
pnpm exec loxtep connectors list --type sdk # optional: see existing SDK connectors
|
|
69
31
|
pnpm exec loxtep ingest provision --name app-events
|
|
32
|
+
pnpm exec loxtep lint
|
|
70
33
|
```
|
|
71
34
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
2. Builds and saves the workflow bundle (`workflow.json` + connection + source data product).
|
|
76
|
-
3. **Deploys** to your attached instance (unless you pass `--no-deploy` or `--dry-run`).
|
|
77
|
-
|
|
78
|
-
Validate first without writing:
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
pnpm exec loxtep ingest provision --name app-events --dry-run
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Option B — Helper script + bundle save
|
|
85
|
-
|
|
86
|
-
Generate bundle JSON only:
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
node node_modules/@loxtep/sdk/docs/examples/generate-ingest-bundle.mjs
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Then persist and deploy via CLI:
|
|
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:
|
|
93
38
|
|
|
94
39
|
```bash
|
|
95
|
-
pnpm exec loxtep
|
|
96
|
-
pnpm exec loxtep
|
|
97
|
-
pnpm exec loxtep
|
|
98
|
-
--project-id <project-id-from-config-list> \
|
|
99
|
-
--instance-id <instance-id-from-config-list>
|
|
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
|
|
100
43
|
```
|
|
101
44
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Poll until deployment completes (Web UI **Observe**, or `loxtep observe status`).
|
|
105
|
-
|
|
106
|
-
### Option C — Loxtep MCP (Cursor / agent)
|
|
107
|
-
|
|
108
|
-
Use **`loxtep_build`** → `save_workflow_bundle` if you prefer MCP authoring.
|
|
109
|
-
Same bundle shape as below; deploy with `loxtep workflows deploy` or MCP
|
|
110
|
-
`deploy_project`.
|
|
111
|
-
|
|
112
|
-
### Option D — Web UI (Studio)
|
|
113
|
-
|
|
114
|
-
Create an **ingestion** workflow with an **SDK** connection node and a **source**
|
|
115
|
-
data product node, then deploy to your attached instance from the Studio deploy
|
|
116
|
-
flow.
|
|
45
|
+
That local package is what you iterate on; deploy pushes it to your instance.
|
|
117
46
|
|
|
118
47
|
---
|
|
119
48
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
Minimal ingestion bundle (SDK connection → source data product). Replace UUIDs
|
|
123
|
-
and names; `connector_id` must be the SDK connector from step 2A or 2B.
|
|
124
|
-
|
|
125
|
-
```json
|
|
126
|
-
{
|
|
127
|
-
"workflow.json": {
|
|
128
|
-
"workflow_id": "<workflow-uuid>",
|
|
129
|
-
"organization_id": "<org-uuid>",
|
|
130
|
-
"project_id": "<project-uuid>",
|
|
131
|
-
"name": "SDK App Events Ingest",
|
|
132
|
-
"workflow_type": "ingestion",
|
|
133
|
-
"domain_id": "<domain-uuid>",
|
|
134
|
-
"status": "active",
|
|
135
|
-
"configuration": {},
|
|
136
|
-
"metadata": {},
|
|
137
|
-
"created_at": "2026-07-23T00:00:00.000Z",
|
|
138
|
-
"updated_at": "2026-07-23T00:00:00.000Z"
|
|
139
|
-
},
|
|
140
|
-
"connections/<connection-uuid>.json": {
|
|
141
|
-
"connection_id": "<connection-uuid>",
|
|
142
|
-
"organization_id": "<org-uuid>",
|
|
143
|
-
"project_id": "<project-uuid>",
|
|
144
|
-
"workflow_id": "<workflow-uuid>",
|
|
145
|
-
"connector_id": "<sdk-connector-uuid>",
|
|
146
|
-
"key": "sdk-input",
|
|
147
|
-
"name": "SDK Input",
|
|
148
|
-
"type": "sdk",
|
|
149
|
-
"status": "active",
|
|
150
|
-
"configuration": {
|
|
151
|
-
"sdk_type": "nodejs",
|
|
152
|
-
"event_type": "app-events"
|
|
153
|
-
},
|
|
154
|
-
"created_at": "2026-07-23T00:00:00.000Z",
|
|
155
|
-
"updated_at": "2026-07-23T00:00:00.000Z"
|
|
156
|
-
},
|
|
157
|
-
"data-products/<data-product-uuid>.json": {
|
|
158
|
-
"data_product_id": "<data-product-uuid>",
|
|
159
|
-
"organization_id": "<org-uuid>",
|
|
160
|
-
"project_id": "<project-uuid>",
|
|
161
|
-
"workflow_id": "<workflow-uuid>",
|
|
162
|
-
"upstream_entity_id": "<connection-uuid>",
|
|
163
|
-
"upstream_entity_type": "connections",
|
|
164
|
-
"domain_id": "<domain-uuid>",
|
|
165
|
-
"name": "app-events",
|
|
166
|
-
"kind": "source",
|
|
167
|
-
"status": "draft",
|
|
168
|
-
"owner": {},
|
|
169
|
-
"governance": {
|
|
170
|
-
"classification": "internal",
|
|
171
|
-
"pii_fields": [],
|
|
172
|
-
"compliance_requirements": [],
|
|
173
|
-
"tags": []
|
|
174
|
-
},
|
|
175
|
-
"metadata": {},
|
|
176
|
-
"created_at": "2026-07-23T00:00:00.000Z",
|
|
177
|
-
"updated_at": "2026-07-23T00:00:00.000Z"
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
The **`name`** on the data product node (`app-events` above) is what you pass
|
|
183
|
-
to `get_writer('app-events')`.
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
### Do not stop at `data-products create`
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
# Creates a catalog row only — get_writer will fail with "not deployed"
|
|
191
|
-
pnpm exec loxtep data-products create --name app-events --domain-id <uuid> --kind source
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Use **`data-products create`** only when you already have (or will immediately
|
|
195
|
-
add) a deployed workflow that sets `deployment_bindings`. For greenfield SDK
|
|
196
|
-
ingest, prefer the bundle + deploy path above.
|
|
197
|
-
|
|
198
|
-
---
|
|
49
|
+
## Phase 3 — Confirm it exists
|
|
199
50
|
|
|
200
|
-
|
|
51
|
+
After deploy:
|
|
201
52
|
|
|
202
53
|
```bash
|
|
203
54
|
pnpm exec loxtep data-products list
|
|
204
|
-
pnpm exec loxtep data-products get <data-product-id>
|
|
205
55
|
```
|
|
206
56
|
|
|
207
|
-
|
|
208
|
-
`queue_name`, `bot_id`, and `instance_id`. If those are missing, finish Phase 2
|
|
209
|
-
before writing.
|
|
210
|
-
|
|
211
|
-
Re-run code generation after platform changes:
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
pnpm exec loxtep generate
|
|
215
|
-
```
|
|
57
|
+
You should see `app-events` (or whatever name you chose).
|
|
216
58
|
|
|
217
59
|
---
|
|
218
60
|
|
|
219
|
-
## Phase 4 — Write
|
|
61
|
+
## Phase 4 — Write from your application
|
|
220
62
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
```bash
|
|
224
|
-
node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
Or embed in your service:
|
|
63
|
+
The SDK reads your workspace (`.loxtep/project.json` + credentials). No manual
|
|
64
|
+
URL or token wiring.
|
|
228
65
|
|
|
229
66
|
```typescript
|
|
230
67
|
import { LoxtepClient } from '@loxtep/sdk';
|
|
231
68
|
|
|
232
69
|
const client = await LoxtepClient.fromWorkspace();
|
|
233
70
|
|
|
234
|
-
// Name from Phase 2 bundle / data-products list (case-sensitive)
|
|
235
71
|
const writer = await client.get_writer('app-events');
|
|
236
72
|
|
|
237
73
|
writer.write({
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
payload: { user_id: 'u_1', action: 'signup' },
|
|
74
|
+
user_id: 'u_1',
|
|
75
|
+
action: 'signup',
|
|
241
76
|
});
|
|
242
77
|
|
|
243
|
-
await writer.close();
|
|
78
|
+
await writer.close();
|
|
244
79
|
```
|
|
245
80
|
|
|
246
|
-
|
|
81
|
+
Pass your **business fields** to `write()`. The SDK wraps them for transport —
|
|
82
|
+
you do not build an envelope or use a `payload` property yourself.
|
|
247
83
|
|
|
248
|
-
|
|
249
|
-
await client.get_writer('app-events', {
|
|
250
|
-
batch_size: 500,
|
|
251
|
-
max_retries: 5,
|
|
252
|
-
});
|
|
253
|
-
```
|
|
84
|
+
Use the same name you passed to `ingest provision`.
|
|
254
85
|
|
|
255
|
-
Read back (
|
|
86
|
+
### Read back (optional)
|
|
256
87
|
|
|
257
88
|
```typescript
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
89
|
+
const client = await LoxtepClient.fromWorkspace();
|
|
90
|
+
const reader = await client.get_reader('app-events');
|
|
91
|
+
|
|
261
92
|
for await (const event of reader) {
|
|
262
93
|
console.log(event);
|
|
263
94
|
break;
|
|
@@ -266,39 +97,45 @@ for await (const event of reader) {
|
|
|
266
97
|
|
|
267
98
|
---
|
|
268
99
|
|
|
269
|
-
##
|
|
100
|
+
## Quick checklist
|
|
270
101
|
|
|
271
102
|
```bash
|
|
272
103
|
pnpm exec loxtep login
|
|
273
104
|
pnpm exec loxtep init
|
|
274
105
|
pnpm exec loxtep attach --instance <instance-id>
|
|
275
|
-
pnpm exec loxtep domains list
|
|
276
|
-
|
|
277
106
|
pnpm exec loxtep ingest provision --name app-events
|
|
278
|
-
|
|
279
|
-
|
|
107
|
+
pnpm exec loxtep lint
|
|
108
|
+
pnpm exec loxtep deploy
|
|
280
109
|
pnpm exec loxtep data-products list
|
|
281
|
-
node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
282
110
|
```
|
|
283
111
|
|
|
112
|
+
Then `get_writer('app-events')` in your app.
|
|
113
|
+
|
|
284
114
|
---
|
|
285
115
|
|
|
286
|
-
##
|
|
116
|
+
## If something goes wrong
|
|
287
117
|
|
|
288
|
-
|
|
|
289
|
-
|
|
|
290
|
-
|
|
|
291
|
-
|
|
|
292
|
-
|
|
|
293
|
-
|
|
|
118
|
+
| What you see | What to do |
|
|
119
|
+
| ------------ | ---------- |
|
|
120
|
+
| Data product not found | Run `data-products list`. Check the name matches `get_writer('…')` exactly. |
|
|
121
|
+
| Not ready to accept writes | Run `ingest provision`, then `deploy` (or `ingest provision --deploy`). Make sure you ran `attach` first. |
|
|
122
|
+
| Multiple products with the same name | Pass the data product id to `get_writer`, or ensure `attach` points at the right instance. |
|
|
123
|
+
| No domain available | Create a domain in the Loxtep UI, or pass `--domain-id` to `ingest provision`. |
|
|
294
124
|
|
|
295
125
|
---
|
|
296
126
|
|
|
297
|
-
##
|
|
127
|
+
## Other ways to create a data product
|
|
128
|
+
|
|
129
|
+
**Loxtep MCP (AI assistant)** — If you use the Loxtep MCP in Cursor, Claude Code,
|
|
130
|
+
or another client, describe what you want in plain language, for example:
|
|
131
|
+
|
|
132
|
+
> Create a data product named `app-events` on my current Loxtep project and
|
|
133
|
+
> deploy it to the instance I already attached. I want to write events from my
|
|
134
|
+
> app with the SDK.
|
|
135
|
+
|
|
136
|
+
The assistant provisions the same end state as `ingest provision`. After that,
|
|
137
|
+
Phase 4 above is unchanged: `get_writer('app-events')` and `write()`.
|
|
298
138
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
| [Quick Reference](./quick-reference.md) | CLI + SDK cheat sheet |
|
|
303
|
-
| [Code-first CLI](./code-first-cli.md) | TypeScript workflow modules + `loxtep deploy` |
|
|
304
|
-
| [Event Replay Cookbook](./event-replay-cookbook.md) | Replay and reprocess |
|
|
139
|
+
**Loxtep Studio (web app)** — *Beta.* You can also design and publish a data
|
|
140
|
+
product in the Loxtep UI. Once it exists on your instance, the SDK code in
|
|
141
|
+
Phase 4 is the same.
|