@streamotter/cli 0.1.0-rc.1
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/LICENSE +21 -0
- package/README.md +104 -0
- package/bin/streamotter.js +2 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +306 -0
- package/dist/cli.js.map +1 -0
- package/dist/generate.d.ts +18 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +171 -0
- package/dist/generate.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +13 -0
- package/dist/main.js.map +1 -0
- package/dist/templates.d.ts +6 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +160 -0
- package/dist/templates.js.map +1 -0
- package/package.json +56 -0
- package/src/cli.ts +302 -0
- package/src/generate.ts +181 -0
- package/src/index.ts +3 -0
- package/src/main.ts +14 -0
- package/src/templates.ts +163 -0
package/dist/generate.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { canonicalJson } from "@streamotter/contracts";
|
|
3
|
+
export const GENERATED_MARKER = "// @generated by streamotter generate";
|
|
4
|
+
const RESERVED = new Set([
|
|
5
|
+
"AppChannels", "ChannelContract", "Client", "Subscription", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Map",
|
|
6
|
+
"Math", "Number", "Object", "Promise", "Record", "Set", "String", "Symbol"
|
|
7
|
+
]);
|
|
8
|
+
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
9
|
+
export function fingerprint(config) {
|
|
10
|
+
return createHash("sha256").update(canonicalJson(config)).digest("hex");
|
|
11
|
+
}
|
|
12
|
+
function pascalCase(id) {
|
|
13
|
+
return id.split(/[-_]+/).filter(Boolean).map(part => part[0].toUpperCase() + part.slice(1)).join("");
|
|
14
|
+
}
|
|
15
|
+
/** Maps schema IDs to unique, valid, non-shadowing TypeScript type names. */
|
|
16
|
+
export function typeNames(schemaIds) {
|
|
17
|
+
const names = new Map();
|
|
18
|
+
const used = new Set();
|
|
19
|
+
for (const id of schemaIds) {
|
|
20
|
+
let name = pascalCase(id);
|
|
21
|
+
if (RESERVED.has(name))
|
|
22
|
+
name = `${name}Schema`;
|
|
23
|
+
let candidate = name;
|
|
24
|
+
for (let suffix = 2; used.has(candidate); suffix++)
|
|
25
|
+
candidate = `${name}${suffix}`;
|
|
26
|
+
used.add(candidate);
|
|
27
|
+
names.set(id, candidate);
|
|
28
|
+
}
|
|
29
|
+
return names;
|
|
30
|
+
}
|
|
31
|
+
function propertyKey(name) {
|
|
32
|
+
return IDENTIFIER.test(name) ? name : JSON.stringify(name);
|
|
33
|
+
}
|
|
34
|
+
function describe(schema) {
|
|
35
|
+
switch (schema.type) {
|
|
36
|
+
case "string": {
|
|
37
|
+
const parts = [];
|
|
38
|
+
if (schema.minLength !== undefined)
|
|
39
|
+
parts.push(`minLength ${schema.minLength}`);
|
|
40
|
+
if (schema.maxLength !== undefined)
|
|
41
|
+
parts.push(`maxLength ${schema.maxLength}`);
|
|
42
|
+
return parts.length > 0 ? parts.join(", ") : null;
|
|
43
|
+
}
|
|
44
|
+
case "number":
|
|
45
|
+
case "integer": {
|
|
46
|
+
const parts = [schema.type];
|
|
47
|
+
if (schema.minimum !== undefined)
|
|
48
|
+
parts.push(`minimum ${schema.minimum}`);
|
|
49
|
+
if (schema.maximum !== undefined)
|
|
50
|
+
parts.push(`maximum ${schema.maximum}`);
|
|
51
|
+
return schema.type === "integer" || parts.length > 1 ? parts.join(", ") : null;
|
|
52
|
+
}
|
|
53
|
+
case "array":
|
|
54
|
+
return `maxItems ${schema.maxItems}`;
|
|
55
|
+
default:
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Renders a schema as a TypeScript type expression (type aliases, never interfaces, so values stay assignable to Json). */
|
|
60
|
+
export function renderType(schema, indent = "") {
|
|
61
|
+
switch (schema.type) {
|
|
62
|
+
case "string":
|
|
63
|
+
return schema.enum !== undefined ? schema.enum.map(value => JSON.stringify(value)).join(" | ") : "string";
|
|
64
|
+
case "number":
|
|
65
|
+
case "integer":
|
|
66
|
+
return "number";
|
|
67
|
+
case "boolean":
|
|
68
|
+
return "boolean";
|
|
69
|
+
case "null":
|
|
70
|
+
return "null";
|
|
71
|
+
case "array": {
|
|
72
|
+
const item = renderType(schema.items, indent);
|
|
73
|
+
return /[|&]/.test(item) && !item.startsWith("{") ? `(${item})[]` : `${item}[]`;
|
|
74
|
+
}
|
|
75
|
+
case "object": {
|
|
76
|
+
const entries = Object.entries(schema.properties);
|
|
77
|
+
if (entries.length === 0)
|
|
78
|
+
return "Record<string, never>";
|
|
79
|
+
const inner = `${indent} `;
|
|
80
|
+
const lines = entries.map(([name, child]) => {
|
|
81
|
+
const note = describe(child);
|
|
82
|
+
const comment = note === null ? "" : `${inner}/** ${note} */\n`;
|
|
83
|
+
const optional = schema.required.includes(name) ? "" : "?";
|
|
84
|
+
return `${comment}${inner}${propertyKey(name)}${optional}: ${renderType(child, inner)};`;
|
|
85
|
+
});
|
|
86
|
+
return `{\n${lines.join("\n")}\n${indent}}`;
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
return "never";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Generates the application channel contract (AppChannels with each channel's
|
|
94
|
+
* parameter type, payload type, and literal version) plus a vanilla TypeScript
|
|
95
|
+
* integration example. Numeric ranges and string lengths remain runtime checks.
|
|
96
|
+
*/
|
|
97
|
+
export function generateFiles(config) {
|
|
98
|
+
const fp = fingerprint(config);
|
|
99
|
+
const header = `${GENERATED_MARKER} (config sha256:${fp}). Do not edit; rerun the generator.\n`;
|
|
100
|
+
const names = typeNames(Object.keys(config.schemas));
|
|
101
|
+
const schemaTypes = Object.entries(config.schemas).map(([id, schema]) => `/** Schema "${id}". */\nexport type ${names.get(id)} = ${renderType(schema)};`);
|
|
102
|
+
const channels = Object.entries(config.channels).map(([name, channel]) => ` ${propertyKey(name)}: ChannelContract<${names.get(channel.paramsSchema)}, ${names.get(channel.payloadSchema)}, ${channel.version}>;`);
|
|
103
|
+
const versions = Object.entries(config.channels).map(([name, channel]) => ` ${propertyKey(name)}: ${channel.version}`);
|
|
104
|
+
const types = [
|
|
105
|
+
header,
|
|
106
|
+
`import type { ChannelContract } from "@streamotter/client";`,
|
|
107
|
+
"",
|
|
108
|
+
...schemaTypes.flatMap(block => [block, ""]),
|
|
109
|
+
`/** Channel contracts for createClient<AppChannels>(). Project "${config.projectId}". */`,
|
|
110
|
+
"export type AppChannels = {",
|
|
111
|
+
...channels,
|
|
112
|
+
"};",
|
|
113
|
+
"",
|
|
114
|
+
"/** Deployed channel versions; subscribe requests must name them exactly. */",
|
|
115
|
+
"export const channelVersions = {",
|
|
116
|
+
versions.join(",\n"),
|
|
117
|
+
"} as const;",
|
|
118
|
+
""
|
|
119
|
+
].join("\n");
|
|
120
|
+
const [firstName, firstChannel] = Object.entries(config.channels)[0] ?? [];
|
|
121
|
+
const paramsSchema = firstChannel === undefined ? undefined : config.schemas[firstChannel.paramsSchema];
|
|
122
|
+
const exampleParams = paramsSchema?.type === "object"
|
|
123
|
+
? `{ ${Object.entries(paramsSchema.properties).map(([key, schema]) => `${propertyKey(key)}: ${schema.type === "string" ? (schema.enum?.[0] !== undefined ? JSON.stringify(schema.enum[0]) : `"example"`) : schema.type === "boolean" ? "true" : "1"}`).join(", ")} }`
|
|
124
|
+
: "{}";
|
|
125
|
+
const example = firstName === undefined ? `${header}\nexport {};\n` : [
|
|
126
|
+
header,
|
|
127
|
+
`import { createClient, type StreamError, type SubscriptionState } from "@streamotter/client";`,
|
|
128
|
+
`import { channelVersions, type AppChannels } from "./streamotter.generated.js";`,
|
|
129
|
+
"",
|
|
130
|
+
"/**",
|
|
131
|
+
` * Subscribes to "${firstName}" and returns a cleanup function. Replace render/showState/showError`,
|
|
132
|
+
" * with your view code. getToken must return your application's session token for the gateway.",
|
|
133
|
+
" */",
|
|
134
|
+
`export async function mount${pascalCase(firstName)}(options: {`,
|
|
135
|
+
" origin: string;",
|
|
136
|
+
` params: AppChannels[${JSON.stringify(firstName)}]["params"];`,
|
|
137
|
+
" getToken: (signal: AbortSignal) => Promise<string>;",
|
|
138
|
+
` render: (data: AppChannels[${JSON.stringify(firstName)}]["data"]) => void;`,
|
|
139
|
+
" showState?: (state: SubscriptionState) => void;",
|
|
140
|
+
" showError?: (error: StreamError) => void;",
|
|
141
|
+
"}): Promise<() => Promise<void>> {",
|
|
142
|
+
" const client = createClient<AppChannels>({ origin: options.origin, getToken: ({ signal }) => options.getToken(signal) });",
|
|
143
|
+
` const subscription = client.subscribe(${JSON.stringify(firstName)}, {`,
|
|
144
|
+
` channelVersion: channelVersions[${JSON.stringify(firstName)}],`,
|
|
145
|
+
" params: options.params",
|
|
146
|
+
" });",
|
|
147
|
+
" subscription.on(\"data\", event => options.render(event.data));",
|
|
148
|
+
" subscription.on(\"state\", ({ state }) => options.showState?.(state));",
|
|
149
|
+
" subscription.on(\"error\", error => options.showError?.(error));",
|
|
150
|
+
" try {",
|
|
151
|
+
" await subscription.ready({ timeoutMs: 30_000 });",
|
|
152
|
+
" } catch (error) {",
|
|
153
|
+
" await subscription.unsubscribe();",
|
|
154
|
+
" await client.close();",
|
|
155
|
+
" throw error;",
|
|
156
|
+
" }",
|
|
157
|
+
" return async () => {",
|
|
158
|
+
" await subscription.unsubscribe();",
|
|
159
|
+
" await client.close();",
|
|
160
|
+
" };",
|
|
161
|
+
"}",
|
|
162
|
+
"",
|
|
163
|
+
`// Example: await mount${pascalCase(firstName)}({ origin: "http://localhost:${config.gateway.port}", params: ${exampleParams}, getToken, render });`,
|
|
164
|
+
""
|
|
165
|
+
].join("\n");
|
|
166
|
+
return [
|
|
167
|
+
{ path: "streamotter.generated.ts", content: types },
|
|
168
|
+
{ path: "streamotter.client.example.ts", content: example }
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACxH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAc,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAsB,MAAM,eAAe,CAAC;AACxH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { runCli } from "./cli.js";
|
|
2
|
+
const shutdownSignal = new Promise(resolve => {
|
|
3
|
+
process.once("SIGINT", () => resolve("SIGINT"));
|
|
4
|
+
process.once("SIGTERM", () => resolve("SIGTERM"));
|
|
5
|
+
});
|
|
6
|
+
const code = await runCli(process.argv.slice(2), {
|
|
7
|
+
out: line => { process.stdout.write(`${line}\n`); },
|
|
8
|
+
err: line => { process.stderr.write(`${line}\n`); },
|
|
9
|
+
shutdownSignal
|
|
10
|
+
});
|
|
11
|
+
// Flush output, then exit even if a dependency left a handle open.
|
|
12
|
+
process.stdout.write("", () => process.stderr.write("", () => process.exit(code)));
|
|
13
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAS,OAAO,CAAC,EAAE;IACnD,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IAC/C,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACnD,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACnD,cAAc;CACf,CAAC,CAAC;AACH,mEAAmE;AACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CAiKpF"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/** Files created by `streamotter init`. The scaffold is fixture-only and has no production credentials. */
|
|
2
|
+
export function scaffoldFiles(projectId) {
|
|
3
|
+
const config = {
|
|
4
|
+
configVersion: 1,
|
|
5
|
+
projectId,
|
|
6
|
+
gateway: { host: "127.0.0.1", port: 7400, path: "/streamotter/socket.io", allowedOrigins: ["http://localhost:5173"] },
|
|
7
|
+
connections: {},
|
|
8
|
+
sources: { jobs: { kind: "fixture", generation: "jobs-fixture-1", fixtureRef: "jobs" } },
|
|
9
|
+
schemas: {
|
|
10
|
+
JobParams: {
|
|
11
|
+
type: "object", additionalProperties: false, required: ["jobId"],
|
|
12
|
+
properties: { jobId: { type: "string", minLength: 1, maxLength: 64 } }
|
|
13
|
+
},
|
|
14
|
+
JobProgress: {
|
|
15
|
+
type: "object", additionalProperties: false, required: ["jobId", "state", "percent"],
|
|
16
|
+
properties: {
|
|
17
|
+
jobId: { type: "string", minLength: 1, maxLength: 64 },
|
|
18
|
+
state: { type: "string", enum: ["queued", "running", "succeeded", "failed"] },
|
|
19
|
+
percent: { type: "integer", minimum: 0, maximum: 100 }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
channels: {
|
|
24
|
+
jobProgress: {
|
|
25
|
+
version: 1, source: "jobs", paramsSchema: "JobParams", payloadSchema: "JobProgress",
|
|
26
|
+
handlersRef: "jobProgress", delivery: { kind: "state", overflow: "resync" }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const handlers = `// @ts-check
|
|
31
|
+
/**
|
|
32
|
+
* Trusted server handlers for StreamOtter, loaded by:
|
|
33
|
+
* streamotter dev --config streamotter.json --handlers server/handlers.mjs
|
|
34
|
+
*
|
|
35
|
+
* StreamOtter loads compiled JavaScript. If you write handlers in TypeScript,
|
|
36
|
+
* compile them first and point --handlers at the output.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Development read model. Replace it with your application's authoritative store.
|
|
41
|
+
* Snapshots and mapped records must describe the same revision progression.
|
|
42
|
+
* @type {Map<string, { revision: string; data: { jobId: string; state: string; percent: number } }>}
|
|
43
|
+
*/
|
|
44
|
+
const jobs = new Map([
|
|
45
|
+
["local/job_1", { revision: "1", data: { jobId: "job_1", state: "queued", percent: 0 } }]
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
/** @type {import("@streamotter/gateway").HandlerRegistry<any>} */
|
|
49
|
+
export const handlers = {
|
|
50
|
+
async authenticate({ token }) {
|
|
51
|
+
// TODO: verify your application's session token and return its Principal
|
|
52
|
+
// ({ subject, tenantId, sessionId, expiresAt, claims }), or null to reject it.
|
|
53
|
+
// Workbench previews use short-lived preview tokens resolved by the gateway itself,
|
|
54
|
+
// so this scaffold intentionally accepts no application tokens yet.
|
|
55
|
+
void token;
|
|
56
|
+
return null;
|
|
57
|
+
},
|
|
58
|
+
channels: {
|
|
59
|
+
jobProgress: {
|
|
60
|
+
async authorize({ principal, params }) {
|
|
61
|
+
return jobs.has(\`\${principal.tenantId}/\${params.jobId}\`);
|
|
62
|
+
},
|
|
63
|
+
map({ record }) {
|
|
64
|
+
const value = /** @type {{ tenantId: string; revision: string; job: { jobId: string; state: string; percent: number } }} */ (record.value);
|
|
65
|
+
// Development only: keep the in-memory read model in step with the fixture stream.
|
|
66
|
+
const key = \`\${value.tenantId}/\${value.job.jobId}\`;
|
|
67
|
+
const current = jobs.get(key);
|
|
68
|
+
if (current === undefined || BigInt(value.revision) > BigInt(current.revision)) {
|
|
69
|
+
jobs.set(key, { revision: value.revision, data: value.job });
|
|
70
|
+
}
|
|
71
|
+
return [{ tenantId: value.tenantId, params: { jobId: value.job.jobId }, revision: value.revision, data: value.job }];
|
|
72
|
+
},
|
|
73
|
+
async snapshot({ principal, params }) {
|
|
74
|
+
const job = jobs.get(\`\${principal.tenantId}/\${params.jobId}\`);
|
|
75
|
+
if (job === undefined) throw new Error("Unknown job");
|
|
76
|
+
return { revision: job.revision, data: job.data };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Development-only principals and fixture records. Never used by \`streamotter start\`. */
|
|
83
|
+
export const development = {
|
|
84
|
+
principals: {
|
|
85
|
+
developer: { subject: "developer", tenantId: "local", sessionId: "dev-session", expiresAt: "2099-01-01T00:00:00.000Z", claims: {} }
|
|
86
|
+
},
|
|
87
|
+
fixtures: {
|
|
88
|
+
jobs: [
|
|
89
|
+
{ key: "job_1", value: { tenantId: "local", revision: "2", job: { jobId: "job_1", state: "running", percent: 25 } } },
|
|
90
|
+
{ key: "job_1", value: { tenantId: "local", revision: "3", job: { jobId: "job_1", state: "running", percent: 60 } } },
|
|
91
|
+
{ key: "job_1", value: { tenantId: "local", revision: "4", job: { jobId: "job_1", state: "running", percent: 90 } } },
|
|
92
|
+
{ key: "job_1", value: { tenantId: "local", revision: "5", job: { jobId: "job_1", state: "succeeded", percent: 100 } } }
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
`;
|
|
97
|
+
const example = `import { createClient } from "@streamotter/client";
|
|
98
|
+
import { channelVersions, type AppChannels } from "../generated/streamotter.generated.js";
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Renders one job's live progress. Run \`streamotter generate --config streamotter.json --out generated\`
|
|
102
|
+
* first. getToken must return your application's session token (or, during local development,
|
|
103
|
+
* a preview token from the workbench).
|
|
104
|
+
*/
|
|
105
|
+
export async function watchJob(jobId: string, getToken: () => Promise<string>, element: HTMLElement): Promise<() => Promise<void>> {
|
|
106
|
+
const client = createClient<AppChannels>({ origin: "http://localhost:7400", getToken: () => getToken() });
|
|
107
|
+
const job = client.subscribe("jobProgress", { channelVersion: channelVersions.jobProgress, params: { jobId } });
|
|
108
|
+
job.on("data", ({ data, revision }) => {
|
|
109
|
+
element.textContent = \`\${data.state} — \${data.percent}% (revision \${revision})\`;
|
|
110
|
+
});
|
|
111
|
+
job.on("state", ({ state }) => {
|
|
112
|
+
element.dataset["delivery"] = state; // "live" means synchronized; anything else may be stale.
|
|
113
|
+
});
|
|
114
|
+
job.on("error", error => console.warn(\`[\${error.code}] \${error.message}\`));
|
|
115
|
+
await job.ready();
|
|
116
|
+
return async () => {
|
|
117
|
+
await job.unsubscribe();
|
|
118
|
+
await client.close();
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
`;
|
|
122
|
+
const readme = `# ${projectId}
|
|
123
|
+
|
|
124
|
+
A StreamOtter V1 project created by \`streamotter init\`. It uses a deterministic fixture source,
|
|
125
|
+
so no Kafka broker is needed to start.
|
|
126
|
+
|
|
127
|
+
## Run locally
|
|
128
|
+
|
|
129
|
+
\`\`\`bash
|
|
130
|
+
streamotter validate --config streamotter.json
|
|
131
|
+
streamotter dev --config streamotter.json --handlers server/handlers.mjs
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
\`dev\` prints the workbench URL and a one-time management token. In the workbench, create a
|
|
135
|
+
preview session for the \`developer\` principal, subscribe to \`jobProgress\` with \`{"jobId": "job_1"}\`,
|
|
136
|
+
and advance the \`jobs\` fixture to watch revisions arrive.
|
|
137
|
+
|
|
138
|
+
## Integrate
|
|
139
|
+
|
|
140
|
+
\`\`\`bash
|
|
141
|
+
streamotter generate --config streamotter.json --out generated
|
|
142
|
+
\`\`\`
|
|
143
|
+
|
|
144
|
+
\`generated/streamotter.generated.ts\` contains \`AppChannels\`; \`web/example.ts\` shows a subscription
|
|
145
|
+
with cleanup and error handling.
|
|
146
|
+
|
|
147
|
+
## Before production
|
|
148
|
+
|
|
149
|
+
- Implement \`authenticate\` in \`server/handlers.mjs\` with your real session verification.
|
|
150
|
+
- Replace the in-memory read model with your authoritative store.
|
|
151
|
+
- Replace the fixture source with a Kafka source using TLS; \`streamotter start\` rejects fixtures and plaintext Kafka.
|
|
152
|
+
`;
|
|
153
|
+
return [
|
|
154
|
+
{ path: "streamotter.json", content: `${JSON.stringify(config, null, 2)}\n` },
|
|
155
|
+
{ path: "server/handlers.mjs", content: handlers },
|
|
156
|
+
{ path: "web/example.ts", content: example },
|
|
157
|
+
{ path: "README.md", content: readme }
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,MAAM,GAAG;QACb,aAAa,EAAE,CAAC;QAChB,SAAS;QACT,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,wBAAwB,EAAE,cAAc,EAAE,CAAC,uBAAuB,CAAC,EAAE;QACrH,WAAW,EAAE,EAAE;QACf,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE;QACxF,OAAO,EAAE;YACP,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC;gBAChE,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE;aACvE;YACD,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC;gBACpF,UAAU,EAAE;oBACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;oBACtD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE;oBAC7E,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE;iBACvD;aACF;SACF;QACD,QAAQ,EAAE;YACR,WAAW,EAAE;gBACX,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa;gBACnF,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE;aAC5E;SACF;KACF,CAAC;IAEF,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkElB,CAAC;IAEA,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;CAwBjB,CAAC;IAEA,MAAM,MAAM,GAAG,KAAK,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8B9B,CAAC;IAEA,OAAO;QACL,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAC7E,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,QAAQ,EAAE;QAClD,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAE;QAC5C,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE;KACvC,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@streamotter/cli",
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
4
|
+
"description": "StreamOtter command-line interface: init, validate, generate, dev (with the local workbench), and start.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"streamotter",
|
|
7
|
+
"kafka",
|
|
8
|
+
"socket.io",
|
|
9
|
+
"realtime",
|
|
10
|
+
"live-data",
|
|
11
|
+
"state-sync",
|
|
12
|
+
"cli",
|
|
13
|
+
"codegen",
|
|
14
|
+
"typescript"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/jfricano/StreamOtter/tree/main/packages/cli#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/jfricano/StreamOtter/issues"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/jfricano/StreamOtter.git",
|
|
23
|
+
"directory": "packages/cli"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": "Orca Solutions",
|
|
27
|
+
"type": "module",
|
|
28
|
+
"bin": {
|
|
29
|
+
"streamotter": "./bin/streamotter.js"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"bin",
|
|
39
|
+
"dist",
|
|
40
|
+
"src"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=24"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@streamotter/contracts": "0.1.0-rc.1",
|
|
50
|
+
"@streamotter/gateway": "0.1.0-rc.1",
|
|
51
|
+
"@streamotter/workbench": "0.1.0-rc.1"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "24.13.6"
|
|
55
|
+
}
|
|
56
|
+
}
|