@loxtep/sdk 0.7.15 → 0.7.18
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 +44 -3
- package/README.md +19 -11
- package/dist/cli/cli-list-output.d.ts +10 -3
- package/dist/cli/cli-list-output.d.ts.map +1 -1
- package/dist/cli/cli-list-output.js +12 -5
- package/dist/cli/cli-list-output.js.map +1 -1
- package/dist/cli/commands/activity-cmd.d.ts.map +1 -1
- package/dist/cli/commands/activity-cmd.js +1 -4
- package/dist/cli/commands/activity-cmd.js.map +1 -1
- package/dist/cli/commands/attach-cmd.d.ts +2 -2
- package/dist/cli/commands/attach-cmd.d.ts.map +1 -1
- package/dist/cli/commands/attach-cmd.js +28 -6
- package/dist/cli/commands/attach-cmd.js.map +1 -1
- package/dist/cli/commands/bundle-cmd.d.ts +15 -0
- package/dist/cli/commands/bundle-cmd.d.ts.map +1 -0
- package/dist/cli/commands/bundle-cmd.js +44 -0
- package/dist/cli/commands/bundle-cmd.js.map +1 -0
- package/dist/cli/commands/config-cmd.d.ts.map +1 -1
- package/dist/cli/commands/config-cmd.js +4 -3
- package/dist/cli/commands/config-cmd.js.map +1 -1
- package/dist/cli/commands/data-contracts-cmd.js +2 -2
- package/dist/cli/commands/data-products-cmd.js +2 -2
- package/dist/cli/commands/domains-cmd.js +2 -2
- package/dist/cli/commands/improvements-cmd.d.ts.map +1 -1
- package/dist/cli/commands/improvements-cmd.js +1 -4
- package/dist/cli/commands/improvements-cmd.js.map +1 -1
- package/dist/cli/commands/ingest-cmd.d.ts +20 -0
- package/dist/cli/commands/ingest-cmd.d.ts.map +1 -0
- package/dist/cli/commands/ingest-cmd.js +95 -0
- package/dist/cli/commands/ingest-cmd.js.map +1 -0
- package/dist/cli/commands/projects-cmd.js +2 -2
- package/dist/cli/commands/standards-cmd.js +2 -2
- package/dist/cli/commands/triggers-cmd.js +2 -2
- package/dist/cli/commands/workflows-cmd.js +2 -2
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +10 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/index.js +32 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/load-cli-config.d.ts.map +1 -1
- package/dist/cli/load-cli-config.js +3 -0
- package/dist/cli/load-cli-config.js.map +1 -1
- package/dist/cli/project-context.d.ts +19 -0
- package/dist/cli/project-context.d.ts.map +1 -1
- package/dist/cli/project-context.js +14 -0
- package/dist/cli/project-context.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/loxtep-client.d.ts.map +1 -1
- package/dist/client/loxtep-client.js +3 -2
- package/dist/client/loxtep-client.js.map +1 -1
- package/dist/client/workflows-types.d.ts +28 -0
- package/dist/client/workflows-types.d.ts.map +1 -1
- package/dist/client/workflows.d.ts +2 -1
- package/dist/client/workflows.d.ts.map +1 -1
- package/dist/client/workflows.js +7 -0
- package/dist/client/workflows.js.map +1 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +1 -6
- package/dist/config/load.js.map +1 -1
- package/dist/config/streams-partial.d.ts +2 -0
- package/dist/config/streams-partial.d.ts.map +1 -1
- package/dist/config/streams-partial.js +6 -0
- package/dist/config/streams-partial.js.map +1 -1
- package/dist/config/workspace-config.d.ts +9 -0
- package/dist/config/workspace-config.d.ts.map +1 -1
- package/dist/config/workspace-config.js +14 -0
- package/dist/config/workspace-config.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/instance-stream-config.d.ts +15 -0
- package/dist/lib/instance-stream-config.d.ts.map +1 -0
- package/dist/lib/instance-stream-config.js +33 -0
- package/dist/lib/instance-stream-config.js.map +1 -0
- package/dist/lib/sdk-ingest-bundle.d.ts +24 -0
- package/dist/lib/sdk-ingest-bundle.d.ts.map +1 -0
- package/dist/lib/sdk-ingest-bundle.js +73 -0
- package/dist/lib/sdk-ingest-bundle.js.map +1 -0
- package/docs/code-first-cli.md +3 -3
- package/docs/examples/generate-ingest-bundle.mjs +133 -0
- package/docs/examples/write-events.mjs +54 -0
- package/docs/getting-started.md +46 -25
- package/docs/quick-reference.md +18 -4
- package/docs/sdk-first-ingest.md +304 -0
- package/package.json +1 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Greenfield SDK ingest — one-shot provision via CLI (preferred) or bundle JSON for manual save.
|
|
4
|
+
*
|
|
5
|
+
* Run from your Loxtep workspace root (after login, init, attach):
|
|
6
|
+
*
|
|
7
|
+
* pnpm exec loxtep ingest provision --name app-events
|
|
8
|
+
*
|
|
9
|
+
* Or generate bundle JSON only:
|
|
10
|
+
*
|
|
11
|
+
* node node_modules/@loxtep/sdk/docs/examples/generate-ingest-bundle.mjs
|
|
12
|
+
* pnpm exec loxtep bundle save --dry-run
|
|
13
|
+
* pnpm exec loxtep bundle save
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { LoxtepClient, buildSdkIngestBundle } from '@loxtep/sdk';
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
|
|
21
|
+
const DATA_PRODUCT_NAME = process.env.LOXTEP_DATA_PRODUCT_NAME ?? 'app-events';
|
|
22
|
+
const WORKFLOW_NAME = process.env.LOXTEP_WORKFLOW_NAME ?? 'SDK App Events Ingest';
|
|
23
|
+
const AUTO_SAVE = process.env.LOXTEP_AUTO_SAVE === '1';
|
|
24
|
+
|
|
25
|
+
async function main() {
|
|
26
|
+
const client = await LoxtepClient.fromWorkspace();
|
|
27
|
+
|
|
28
|
+
const user = await client.session.get_current_user();
|
|
29
|
+
const organizationId = user.organization_id ?? client.organization_id;
|
|
30
|
+
const projectId = client.project_id;
|
|
31
|
+
const instanceId = client.instance_id;
|
|
32
|
+
|
|
33
|
+
if (!organizationId || !projectId) {
|
|
34
|
+
console.error(
|
|
35
|
+
'Missing organization_id or project_id. Run from a workspace after `loxtep init`.'
|
|
36
|
+
);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
if (!instanceId) {
|
|
40
|
+
console.error('Missing instance_id. Run `loxtep attach --instance <id>` first.');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let domainId = process.env.LOXTEP_DOMAIN_ID;
|
|
45
|
+
if (!domainId) {
|
|
46
|
+
const domains = await client.define.domains.list({ page_size: 20 });
|
|
47
|
+
const items = domains.items ?? [];
|
|
48
|
+
if (items.length === 0) {
|
|
49
|
+
console.error(
|
|
50
|
+
'No domains found. Create one in the Web UI (Governance → Domains) or set LOXTEP_DOMAIN_ID.'
|
|
51
|
+
);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
domainId = items[0].domain_id;
|
|
55
|
+
console.error(`Using domain: ${items[0].name} (${domainId})`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.error('Creating SDK connector…');
|
|
59
|
+
const connector = await client.connect.connectors.create({
|
|
60
|
+
connector_type: 'sdk',
|
|
61
|
+
metadata: { name: 'SDK Connector', created_by: 'generate-ingest-bundle' },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const bundle = buildSdkIngestBundle({
|
|
65
|
+
organization_id: organizationId,
|
|
66
|
+
project_id: projectId,
|
|
67
|
+
domain_id: domainId,
|
|
68
|
+
connector_id: connector.connector_id,
|
|
69
|
+
data_product_name: DATA_PRODUCT_NAME,
|
|
70
|
+
workflow_name: WORKFLOW_NAME,
|
|
71
|
+
user_id: user.user_id,
|
|
72
|
+
workflow_id: randomUUID(),
|
|
73
|
+
connection_id: randomUUID(),
|
|
74
|
+
data_product_id: randomUUID(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
mkdirSync('.loxtep', { recursive: true });
|
|
78
|
+
const outPath = join('.loxtep', 'sdk-ingest-bundle.json');
|
|
79
|
+
writeFileSync(outPath, JSON.stringify({ project_id: projectId, files: bundle.files }, null, 2));
|
|
80
|
+
|
|
81
|
+
console.log(
|
|
82
|
+
JSON.stringify(
|
|
83
|
+
{
|
|
84
|
+
connector_id: connector.connector_id,
|
|
85
|
+
workflow_id: bundle.workflow_id,
|
|
86
|
+
data_product_id: bundle.data_product_id,
|
|
87
|
+
data_product_name: bundle.data_product_name,
|
|
88
|
+
bundle_path: outPath,
|
|
89
|
+
},
|
|
90
|
+
null,
|
|
91
|
+
2
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (AUTO_SAVE) {
|
|
96
|
+
console.error('Saving workflow bundle (LOXTEP_AUTO_SAVE=1)…');
|
|
97
|
+
const saveResult = await client.build.workflows.save_workflow_bundle(projectId, {
|
|
98
|
+
files: bundle.files,
|
|
99
|
+
dry_run: false,
|
|
100
|
+
});
|
|
101
|
+
console.error('Deploying…');
|
|
102
|
+
const deployResult = await client.build.workflows.deploy({
|
|
103
|
+
project_id: projectId,
|
|
104
|
+
instance_id: instanceId,
|
|
105
|
+
});
|
|
106
|
+
console.log(JSON.stringify({ save: saveResult, deploy: deployResult }, null, 2));
|
|
107
|
+
console.error(`
|
|
108
|
+
Provisioned. Write events:
|
|
109
|
+
LOXTEP_DATA_PRODUCT_NAME=${DATA_PRODUCT_NAME} node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
110
|
+
`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.error(`
|
|
115
|
+
Next steps (no MCP required)
|
|
116
|
+
────────────────────────────
|
|
117
|
+
Preferred — one command:
|
|
118
|
+
pnpm exec loxtep ingest provision --name ${DATA_PRODUCT_NAME}
|
|
119
|
+
|
|
120
|
+
Or save the generated bundle:
|
|
121
|
+
pnpm exec loxtep bundle save --dry-run --file ${outPath}
|
|
122
|
+
pnpm exec loxtep bundle save --file ${outPath}
|
|
123
|
+
pnpm exec loxtep workflows deploy --project-id ${projectId} --instance-id ${instanceId}
|
|
124
|
+
|
|
125
|
+
Write events:
|
|
126
|
+
LOXTEP_DATA_PRODUCT_NAME=${DATA_PRODUCT_NAME} node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
127
|
+
`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
main().catch(err => {
|
|
131
|
+
console.error(err instanceof Error ? err.message : err);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Write sample events to a deployed source data product via get_writer.
|
|
4
|
+
*
|
|
5
|
+
* Prerequisites: login, init, attach, SDK ingest workflow deployed (see sdk-first-ingest.md).
|
|
6
|
+
*
|
|
7
|
+
* node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
8
|
+
*
|
|
9
|
+
* Env:
|
|
10
|
+
* LOXTEP_DATA_PRODUCT_NAME default: app-events
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { LoxtepClient } from '@loxtep/sdk';
|
|
14
|
+
|
|
15
|
+
const DATA_PRODUCT_NAME = process.env.LOXTEP_DATA_PRODUCT_NAME ?? 'app-events';
|
|
16
|
+
|
|
17
|
+
async function main() {
|
|
18
|
+
const client = await LoxtepClient.fromWorkspace();
|
|
19
|
+
|
|
20
|
+
console.error(`Opening writer for data product "${DATA_PRODUCT_NAME}"…`);
|
|
21
|
+
const writer = await client.get_writer(DATA_PRODUCT_NAME);
|
|
22
|
+
|
|
23
|
+
const events = [
|
|
24
|
+
{
|
|
25
|
+
event_id: `evt_${Date.now()}_1`,
|
|
26
|
+
occurred_at: new Date().toISOString(),
|
|
27
|
+
payload: { source: 'write-events.mjs', action: 'example_write', index: 1 },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
event_id: `evt_${Date.now()}_2`,
|
|
31
|
+
occurred_at: new Date().toISOString(),
|
|
32
|
+
payload: { source: 'write-events.mjs', action: 'example_write', index: 2 },
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
for (const event of events) {
|
|
37
|
+
writer.write(event);
|
|
38
|
+
console.log(JSON.stringify({ written: event.event_id }));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await writer.close();
|
|
42
|
+
console.error('Writer closed — events flushed.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
main().catch(err => {
|
|
46
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
47
|
+
console.error(message);
|
|
48
|
+
if (/not deployed/i.test(message)) {
|
|
49
|
+
console.error(
|
|
50
|
+
'\nDeploy an SDK ingestion workflow first. See nodejs/docs/sdk-first-ingest.md'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
process.exit(1);
|
|
54
|
+
});
|
package/docs/getting-started.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# SDK Getting Started Guide
|
|
2
2
|
|
|
3
3
|
Get from zero to a working Loxtep workspace: authenticate, scaffold a project,
|
|
4
|
-
attach to a runtime instance,
|
|
5
|
-
|
|
4
|
+
attach to a runtime instance, **provision your first source data product**, and
|
|
5
|
+
write events with `get_writer`.
|
|
6
6
|
|
|
7
|
+
> **SDK-first ingest (login → init → attach → write):**
|
|
8
|
+
> [SDK-first ingest](./sdk-first-ingest.md) — the primary path when your app
|
|
9
|
+
> sends events via the SDK (no SaaS connector first).
|
|
10
|
+
>
|
|
7
11
|
> **Other paths:** [Code-first CLI](./code-first-cli.md) (`init → attach →
|
|
8
12
|
> generate → test → deploy`), [Agent-first MCP](https://github.com/LoxtepInc/loxtep-plugins-skills),
|
|
9
13
|
> or the **Web UI** at [app.loxtep.io](https://app.loxtep.io).
|
|
@@ -17,9 +21,10 @@ comes **after** you deploy a workflow that publishes a data product.
|
|
|
17
21
|
- [Step 3: Scaffold a Workspace](#step-3-scaffold-a-workspace)
|
|
18
22
|
- [Step 4: Attach to an Instance](#step-4-attach-to-an-instance)
|
|
19
23
|
- [Step 5: Generate Typed Constants](#step-5-generate-typed-constants)
|
|
20
|
-
- [Step 6:
|
|
21
|
-
- [Step 7:
|
|
22
|
-
- [Step 8:
|
|
24
|
+
- [Step 6: Your first data product (SDK ingest)](#step-6-your-first-data-product-sdk-ingest)
|
|
25
|
+
- [Step 7: Explore the platform (optional)](#step-7-explore-the-platform-optional)
|
|
26
|
+
- [Step 8: Use the SDK in application code](#step-8-use-the-sdk-in-application-code)
|
|
27
|
+
- [Step 9: Write and read events](#step-9-write-and-read-events)
|
|
23
28
|
- [Troubleshooting](#troubleshooting)
|
|
24
29
|
- [Next Steps](#next-steps)
|
|
25
30
|
|
|
@@ -158,7 +163,33 @@ products, connectors, domains, queues, workflows). Re-run after platform changes
|
|
|
158
163
|
|
|
159
164
|
---
|
|
160
165
|
|
|
161
|
-
## Step 6:
|
|
166
|
+
## Step 6: Your first data product (SDK ingest)
|
|
167
|
+
|
|
168
|
+
After `attach`, the usual greenfield goal is: **provision a source data product
|
|
169
|
+
on your instance, then call `get_writer` from your app.**
|
|
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:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pnpm exec loxtep domains list
|
|
179
|
+
|
|
180
|
+
# One command: SDK connector + workflow bundle + deploy (no MCP)
|
|
181
|
+
pnpm exec loxtep ingest provision --name app-events
|
|
182
|
+
|
|
183
|
+
pnpm exec loxtep data-products list
|
|
184
|
+
node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Do **not** expect `pnpm exec loxtep data-products create` alone to enable
|
|
188
|
+
streaming — it registers catalog metadata without runtime bindings.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Step 7: Explore the platform (optional)
|
|
162
193
|
|
|
163
194
|
Run these **from your workspace directory** (where `.loxtep/project.json` lives)
|
|
164
195
|
after Steps 3–4:
|
|
@@ -178,12 +209,12 @@ pnpm exec loxtep projects list
|
|
|
178
209
|
pnpm exec loxtep workflows list --project-id <project-id-from-list>
|
|
179
210
|
```
|
|
180
211
|
|
|
181
|
-
On a new account, **data products may be empty** until you
|
|
182
|
-
|
|
212
|
+
On a new account, **data products may be empty** until you complete
|
|
213
|
+
[Step 6](#step-6-your-first-data-product-sdk-ingest). That's expected.
|
|
183
214
|
|
|
184
215
|
---
|
|
185
216
|
|
|
186
|
-
## Step
|
|
217
|
+
## Step 8: Use the SDK in application code
|
|
187
218
|
|
|
188
219
|
Prefer workspace auto-config instead of hand-building `api_url` and tokens:
|
|
189
220
|
|
|
@@ -209,33 +240,22 @@ const instances = await client.workspace.instances.list();
|
|
|
209
240
|
|
|
210
241
|
---
|
|
211
242
|
|
|
212
|
-
## Step
|
|
213
|
-
|
|
214
|
-
`get_writer` and `get_reader` resolve queue, bot, and stream configuration from
|
|
215
|
-
**deployment metadata**. They only work for data products that exist **and** are
|
|
216
|
-
deployed on your attached instance.
|
|
217
|
-
|
|
218
|
-
1. Author a workflow under `workflows/` (see [Code-first CLI](./code-first-cli.md)).
|
|
219
|
-
2. `pnpm exec loxtep deploy`
|
|
220
|
-
3. Confirm the data product name:
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
pnpm exec loxtep data-products list
|
|
224
|
-
```
|
|
243
|
+
## Step 9: Write and read events
|
|
225
244
|
|
|
226
|
-
|
|
245
|
+
After [Step 6](#step-6-your-first-data-product-sdk-ingest), stream from
|
|
246
|
+
application code:
|
|
227
247
|
|
|
228
248
|
```typescript
|
|
229
249
|
const client = await LoxtepClient.fromWorkspace();
|
|
230
250
|
|
|
231
|
-
const writer = await client.get_writer('
|
|
251
|
+
const writer = await client.get_writer('app-events'); // name from Step 6 / data-products list
|
|
232
252
|
writer.write({
|
|
233
253
|
order_id: 'ord_1',
|
|
234
254
|
total: 99.5,
|
|
235
255
|
});
|
|
236
256
|
await writer.close();
|
|
237
257
|
|
|
238
|
-
const reader = await client.get_reader('
|
|
258
|
+
const reader = await client.get_reader('app-events', {
|
|
239
259
|
bot_id: 'my-app-reader',
|
|
240
260
|
});
|
|
241
261
|
for await (const event of reader) {
|
|
@@ -318,6 +338,7 @@ data product UUID instead of a name.
|
|
|
318
338
|
|
|
319
339
|
| Resource | Description |
|
|
320
340
|
|----------|-------------|
|
|
341
|
+
| [SDK-first ingest](./sdk-first-ingest.md) | Greenfield: provision + `get_writer` |
|
|
321
342
|
| [Code-first CLI guide](./code-first-cli.md) | Workflow modules, `test`, `deploy` |
|
|
322
343
|
| [Quick Reference Card](./quick-reference.md) | Cheat sheet for common operations |
|
|
323
344
|
| [Event Replay Cookbook](./event-replay-cookbook.md) | Replay and reprocess historical events |
|
package/docs/quick-reference.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# SDK Quick Reference Card
|
|
2
2
|
|
|
3
3
|
Concise cheat sheet for common Loxtep SDK operations (Node.js).
|
|
4
|
-
For full walkthroughs, see
|
|
5
|
-
or [Code-first CLI guide](./code-first-cli.md).
|
|
4
|
+
For full walkthroughs, see [SDK-first ingest](./sdk-first-ingest.md),
|
|
5
|
+
[Getting Started Guide](./getting-started.md), or [Code-first CLI guide](./code-first-cli.md).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -183,10 +183,24 @@ const hits = await client.query.catalog.search({ query: 'orders' });
|
|
|
183
183
|
Workspace lifecycle (requires `loxtep init` first — see [Code-first CLI guide](./code-first-cli.md)):
|
|
184
184
|
|
|
185
185
|
```bash
|
|
186
|
-
pnpm exec loxtep init [--template shopify-orders]
|
|
187
186
|
pnpm exec loxtep login
|
|
188
|
-
pnpm exec loxtep
|
|
187
|
+
pnpm exec loxtep init
|
|
188
|
+
pnpm exec loxtep attach --instance <instance-id>
|
|
189
189
|
pnpm exec loxtep generate
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**SDK-first ingest** (after attach — see [SDK-first ingest](./sdk-first-ingest.md)):
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
pnpm exec loxtep domains list
|
|
196
|
+
node node_modules/@loxtep/sdk/docs/examples/generate-ingest-bundle.mjs
|
|
197
|
+
pnpm exec loxtep workflows deploy --project-id <id> --instance-id <id>
|
|
198
|
+
node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Code-first workflow modules:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
190
204
|
pnpm exec loxtep test my-module --event ./events/sample.json
|
|
191
205
|
pnpm exec loxtep deploy
|
|
192
206
|
```
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# SDK-first ingest — your first data product and `get_writer`
|
|
2
|
+
|
|
3
|
+
You logged in, ran `init`, and `attach`. This guide takes you from there to
|
|
4
|
+
**writing events from application code** with `client.get_writer(name)`.
|
|
5
|
+
|
|
6
|
+
That is the primary greenfield path when you are **not** wiring a SaaS connector
|
|
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).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What you are building
|
|
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
|
+
```
|
|
24
|
+
|
|
25
|
+
`get_writer` resolves **queue**, **bot**, and **stream bus** config from
|
|
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`):
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm exec loxtep config list # project_id + instance_id should be set
|
|
38
|
+
pnpm exec loxtep domains list # you need a domain_id for the data product
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Every org has at least one domain. Copy a `domain_id` from the list.
|
|
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.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Phase 2 — Provision runtime (required before `get_writer`)
|
|
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)
|
|
65
|
+
|
|
66
|
+
From your workspace root (after `login`, `init`, `attach`):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pnpm exec loxtep ingest provision --name app-events
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This command:
|
|
73
|
+
|
|
74
|
+
1. Creates an org-level **SDK connector** (`connector_type: "sdk"`).
|
|
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:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pnpm exec loxtep bundle save --dry-run --file .loxtep/sdk-ingest-bundle.json
|
|
96
|
+
pnpm exec loxtep bundle save --file .loxtep/sdk-ingest-bundle.json
|
|
97
|
+
pnpm exec loxtep workflows deploy \
|
|
98
|
+
--project-id <project-id-from-config-list> \
|
|
99
|
+
--instance-id <instance-id-from-config-list>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Or set `LOXTEP_AUTO_SAVE=1` on the helper script to save + deploy in one step.
|
|
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.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### Workflow bundle shape
|
|
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
|
+
---
|
|
199
|
+
|
|
200
|
+
## Phase 3 — Verify deployment
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
pnpm exec loxtep data-products list
|
|
204
|
+
pnpm exec loxtep data-products get <data-product-id>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
A deployable source data product includes **`deployment_bindings`** with
|
|
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
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Phase 4 — Write events from your app
|
|
220
|
+
|
|
221
|
+
Copy or run the example:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Or embed in your service:
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { LoxtepClient } from '@loxtep/sdk';
|
|
231
|
+
|
|
232
|
+
const client = await LoxtepClient.fromWorkspace();
|
|
233
|
+
|
|
234
|
+
// Name from Phase 2 bundle / data-products list (case-sensitive)
|
|
235
|
+
const writer = await client.get_writer('app-events');
|
|
236
|
+
|
|
237
|
+
writer.write({
|
|
238
|
+
event_id: 'evt_001',
|
|
239
|
+
occurred_at: new Date().toISOString(),
|
|
240
|
+
payload: { user_id: 'u_1', action: 'signup' },
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await writer.close(); // flush buffered events
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Optional writer tuning:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
await client.get_writer('app-events', {
|
|
250
|
+
batch_size: 500,
|
|
251
|
+
max_retries: 5,
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Read back (after data has landed):
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
const reader = await client.get_reader('app-events', {
|
|
259
|
+
bot_id: 'my-app-reader',
|
|
260
|
+
});
|
|
261
|
+
for await (const event of reader) {
|
|
262
|
+
console.log(event);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## End-to-end checklist
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
pnpm exec loxtep login
|
|
273
|
+
pnpm exec loxtep init
|
|
274
|
+
pnpm exec loxtep attach --instance <instance-id>
|
|
275
|
+
pnpm exec loxtep domains list
|
|
276
|
+
|
|
277
|
+
pnpm exec loxtep ingest provision --name app-events
|
|
278
|
+
# or: generate-ingest-bundle.mjs + loxtep bundle save
|
|
279
|
+
|
|
280
|
+
pnpm exec loxtep data-products list
|
|
281
|
+
node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Troubleshooting
|
|
287
|
+
|
|
288
|
+
| Symptom | Cause | Fix |
|
|
289
|
+
| ------- | ----- | --- |
|
|
290
|
+
| `StreamingError: … is not deployed` | No `deployment_bindings` | Complete Phase 2 (bundle + deploy) |
|
|
291
|
+
| `NotFoundError: Data product '…' not found` | Wrong name or wrong instance | `data-products list`; check `attach` / `instance_id` |
|
|
292
|
+
| `AmbiguityError: Multiple data products match` | Same name on multiple instances | Use UUID or set `instance_id` in project.json |
|
|
293
|
+
| Empty `domains list` | New org / permissions | Create a domain in the Web UI (Governance) |
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Next steps
|
|
298
|
+
|
|
299
|
+
| Resource | Description |
|
|
300
|
+
| -------- | ----------- |
|
|
301
|
+
| [Getting Started](./getting-started.md) | Login, init, attach, generate |
|
|
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 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loxtep/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.18",
|
|
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",
|