@semilayer/cli 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-config-3MWVCUTJ.js +117 -0
- package/dist/auth-config-3MWVCUTJ.js.map +1 -0
- package/dist/billing-OY5GJP5X.js +265 -0
- package/dist/billing-OY5GJP5X.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +49 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-7TA63VHV.js +38 -0
- package/dist/chunk-7TA63VHV.js.map +1 -0
- package/dist/chunk-ALA4X7UU.js +19 -0
- package/dist/chunk-ALA4X7UU.js.map +1 -0
- package/dist/chunk-NIDLPHWY.js +53 -0
- package/dist/chunk-NIDLPHWY.js.map +1 -0
- package/dist/chunk-QMF7LD67.js +39 -0
- package/dist/chunk-QMF7LD67.js.map +1 -0
- package/dist/chunk-QXIVJY7K.js +56 -0
- package/dist/chunk-QXIVJY7K.js.map +1 -0
- package/dist/chunk-T3UROBMA.js +169 -0
- package/dist/chunk-T3UROBMA.js.map +1 -0
- package/dist/chunk-WZYOSGN3.js +88 -0
- package/dist/chunk-WZYOSGN3.js.map +1 -0
- package/dist/config-DACYO7JC.js +103 -0
- package/dist/config-DACYO7JC.js.map +1 -0
- package/dist/dev-R3AZSONQ.js +57 -0
- package/dist/dev-R3AZSONQ.js.map +1 -0
- package/dist/envs-RNZQ3OQP.js +105 -0
- package/dist/envs-RNZQ3OQP.js.map +1 -0
- package/dist/export-YRFR3JH2.js +81 -0
- package/dist/export-YRFR3JH2.js.map +1 -0
- package/dist/generate-QUETX3TN.js +41 -0
- package/dist/generate-QUETX3TN.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/init-TWJAGUN3.js +187 -0
- package/dist/init-TWJAGUN3.js.map +1 -0
- package/dist/keys-JBKCYKJU.js +111 -0
- package/dist/keys-JBKCYKJU.js.map +1 -0
- package/dist/lenses-VZSDFH3D.js +51 -0
- package/dist/lenses-VZSDFH3D.js.map +1 -0
- package/dist/login-BZ6ZPFHC.js +119 -0
- package/dist/login-BZ6ZPFHC.js.map +1 -0
- package/dist/logout-VMPRV62T.js +38 -0
- package/dist/logout-VMPRV62T.js.map +1 -0
- package/dist/members-DVE5FDLZ.js +110 -0
- package/dist/members-DVE5FDLZ.js.map +1 -0
- package/dist/observe-W346RZBX.js +149 -0
- package/dist/observe-W346RZBX.js.map +1 -0
- package/dist/orgs-YA3TVA3T.js +67 -0
- package/dist/orgs-YA3TVA3T.js.map +1 -0
- package/dist/pause-GQ6PKBUA.js +50 -0
- package/dist/pause-GQ6PKBUA.js.map +1 -0
- package/dist/projects-DMA2AXH3.js +107 -0
- package/dist/projects-DMA2AXH3.js.map +1 -0
- package/dist/push-3ZK3W2AC.js +145 -0
- package/dist/push-3ZK3W2AC.js.map +1 -0
- package/dist/resume-KVRPLXZZ.js +50 -0
- package/dist/resume-KVRPLXZZ.js.map +1 -0
- package/dist/run-IR5B4AE3.js +375 -0
- package/dist/run-IR5B4AE3.js.map +1 -0
- package/dist/sources-S52HUWRK.js +170 -0
- package/dist/sources-S52HUWRK.js.map +1 -0
- package/dist/status-AUECH6RX.js +130 -0
- package/dist/status-AUECH6RX.js.map +1 -0
- package/dist/stream-V7RGHTPR.js +344 -0
- package/dist/stream-V7RGHTPR.js.map +1 -0
- package/dist/sync-NRTC3WX4.js +68 -0
- package/dist/sync-NRTC3WX4.js.map +1 -0
- package/dist/whoami-EQGW6V5D.js +50 -0
- package/dist/whoami-EQGW6V5D.js.map +1 -0
- package/dist/wizard-QLAR33T2.js +306 -0
- package/dist/wizard-QLAR33T2.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
confirm,
|
|
4
|
+
input,
|
|
5
|
+
secret,
|
|
6
|
+
select
|
|
7
|
+
} from "./chunk-QXIVJY7K.js";
|
|
8
|
+
import {
|
|
9
|
+
createApiClient
|
|
10
|
+
} from "./chunk-T3UROBMA.js";
|
|
11
|
+
import "./chunk-7TA63VHV.js";
|
|
12
|
+
import {
|
|
13
|
+
bold,
|
|
14
|
+
dim,
|
|
15
|
+
error,
|
|
16
|
+
info,
|
|
17
|
+
newline,
|
|
18
|
+
success
|
|
19
|
+
} from "./chunk-WZYOSGN3.js";
|
|
20
|
+
|
|
21
|
+
// src/commands/wizard.ts
|
|
22
|
+
import { defineCommand } from "citty";
|
|
23
|
+
import {
|
|
24
|
+
mapDbTypeToFieldType
|
|
25
|
+
} from "@semilayer/core";
|
|
26
|
+
var wizard_default = defineCommand({
|
|
27
|
+
meta: {
|
|
28
|
+
name: "wizard",
|
|
29
|
+
description: "Interactive setup \u2014 connect a database and get a publishable key in seconds"
|
|
30
|
+
},
|
|
31
|
+
args: {
|
|
32
|
+
"service-url": {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Service API URL"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
async run({ args: _args }) {
|
|
38
|
+
try {
|
|
39
|
+
const api = await createApiClient();
|
|
40
|
+
console.log();
|
|
41
|
+
console.log(` ${bold("SemiLayer Quick Connect")}`);
|
|
42
|
+
console.log(` ${"\u2500".repeat(23)}`);
|
|
43
|
+
const me = await api.get("/auth/me");
|
|
44
|
+
if (me.orgs.length === 0) {
|
|
45
|
+
error("No organizations found. Run `semilayer orgs create` first.");
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let orgSlug;
|
|
50
|
+
if (me.orgs.length === 1) {
|
|
51
|
+
orgSlug = me.orgs[0].slug;
|
|
52
|
+
info(`Org: ${bold(orgSlug)}`);
|
|
53
|
+
} else {
|
|
54
|
+
newline();
|
|
55
|
+
console.log(` ${bold("Step 1 \u2014 Organization")}`);
|
|
56
|
+
orgSlug = await select("Select an org:", me.orgs.map((o) => o.slug));
|
|
57
|
+
}
|
|
58
|
+
newline();
|
|
59
|
+
console.log(` ${bold("Step 1 \u2014 Project")}`);
|
|
60
|
+
const { projects } = await api.get(
|
|
61
|
+
`/v1/orgs/${orgSlug}/projects`
|
|
62
|
+
);
|
|
63
|
+
let projectSlug;
|
|
64
|
+
let publishableKey = null;
|
|
65
|
+
let autoEnvSlug = null;
|
|
66
|
+
const projectChoices = [
|
|
67
|
+
...projects.map((p) => p.slug),
|
|
68
|
+
"+ Create new project"
|
|
69
|
+
];
|
|
70
|
+
const projectChoice = await select("Select a project:", projectChoices);
|
|
71
|
+
if (projectChoice === "+ Create new project") {
|
|
72
|
+
const name = await input("Project name", "My Project");
|
|
73
|
+
const slug = await input(
|
|
74
|
+
"Slug",
|
|
75
|
+
name.toLowerCase().replace(/[^a-z0-9]+/g, "-")
|
|
76
|
+
);
|
|
77
|
+
const res = await api.post(
|
|
78
|
+
`/v1/orgs/${orgSlug}/projects`,
|
|
79
|
+
{ name, slug }
|
|
80
|
+
);
|
|
81
|
+
projectSlug = res.project.slug;
|
|
82
|
+
autoEnvSlug = "development";
|
|
83
|
+
const pk = res.keys?.find((k) => k.type === "publishable");
|
|
84
|
+
if (pk) publishableKey = pk.key;
|
|
85
|
+
success(`Created project ${bold(projectSlug)} with Development environment`);
|
|
86
|
+
} else {
|
|
87
|
+
projectSlug = projectChoice;
|
|
88
|
+
info(`Project: ${bold(projectSlug)}`);
|
|
89
|
+
}
|
|
90
|
+
let envSlug;
|
|
91
|
+
if (autoEnvSlug) {
|
|
92
|
+
envSlug = autoEnvSlug;
|
|
93
|
+
success(`Using ${bold("development")} environment`);
|
|
94
|
+
} else {
|
|
95
|
+
newline();
|
|
96
|
+
console.log(` ${bold("Step 2 \u2014 Environment")}`);
|
|
97
|
+
const { environments } = await api.get(
|
|
98
|
+
`/v1/orgs/${orgSlug}/projects/${projectSlug}/envs`
|
|
99
|
+
);
|
|
100
|
+
const envChoices = [
|
|
101
|
+
...environments.map((e) => e.slug),
|
|
102
|
+
"+ Create new environment"
|
|
103
|
+
];
|
|
104
|
+
const envChoice = await select("Select an environment:", envChoices);
|
|
105
|
+
if (envChoice === "+ Create new environment") {
|
|
106
|
+
const name = await input("Environment name", "Staging");
|
|
107
|
+
const slug = await input(
|
|
108
|
+
"Slug",
|
|
109
|
+
name.toLowerCase().replace(/[^a-z0-9]+/g, "-")
|
|
110
|
+
);
|
|
111
|
+
const res = await api.post(
|
|
112
|
+
`/v1/orgs/${orgSlug}/projects/${projectSlug}/envs`,
|
|
113
|
+
{ name, slug }
|
|
114
|
+
);
|
|
115
|
+
envSlug = res.environment.slug;
|
|
116
|
+
const pk = res.keys?.find((k) => k.type === "publishable");
|
|
117
|
+
if (pk) publishableKey = pk.key;
|
|
118
|
+
success(`Created environment ${bold(envSlug)}`);
|
|
119
|
+
} else {
|
|
120
|
+
envSlug = envChoice;
|
|
121
|
+
info(`Environment: ${bold(envSlug)}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const envPath = `/v1/orgs/${orgSlug}/projects/${projectSlug}/envs/${envSlug}`;
|
|
125
|
+
if (!publishableKey) {
|
|
126
|
+
try {
|
|
127
|
+
const keysRes = await api.get(`${envPath}/keys?reveal=true`);
|
|
128
|
+
const pk = keysRes.keys.find((k) => k.type === "publishable");
|
|
129
|
+
if (pk) publishableKey = pk.key;
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
newline();
|
|
134
|
+
const configureAuth = await confirm("Configure end-user authentication? (JWKS from Auth0/Clerk/etc.)", false);
|
|
135
|
+
if (configureAuth) {
|
|
136
|
+
const jwksUrl = await input("JWKS URL");
|
|
137
|
+
const authIssuer = await input("Issuer");
|
|
138
|
+
const authAudience = await input("Audience (optional, press Enter to skip)");
|
|
139
|
+
try {
|
|
140
|
+
await api.patch(`${envPath}/auth`, {
|
|
141
|
+
jwksUrl,
|
|
142
|
+
issuer: authIssuer,
|
|
143
|
+
...authAudience ? { audience: authAudience } : {}
|
|
144
|
+
});
|
|
145
|
+
success("Auth provider configured");
|
|
146
|
+
info(`Lens rules can now use ${bold("'authenticated'")} and claim checks`);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
error(`Failed to configure auth: ${err.message}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
newline();
|
|
152
|
+
console.log(` ${bold("Step 3 \u2014 Connect Database")}`);
|
|
153
|
+
const bridge = "@semilayer/bridge-postgres";
|
|
154
|
+
info(`Bridge: ${bold("PostgreSQL")}`);
|
|
155
|
+
let connectionUrl = "";
|
|
156
|
+
let tables = [];
|
|
157
|
+
while (true) {
|
|
158
|
+
connectionUrl = await secret("Connection URL");
|
|
159
|
+
if (!connectionUrl) {
|
|
160
|
+
error("Connection URL is required");
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
info("Testing connection...");
|
|
164
|
+
const res = await api.post(
|
|
165
|
+
`/v1/orgs/${orgSlug}/introspect`,
|
|
166
|
+
{ bridge, config: { url: connectionUrl } }
|
|
167
|
+
);
|
|
168
|
+
if (res.connected) {
|
|
169
|
+
tables = res.targets ?? [];
|
|
170
|
+
success(`Connected \u2014 ${tables.length} tables found`);
|
|
171
|
+
break;
|
|
172
|
+
} else {
|
|
173
|
+
error(`Connection failed: ${res.error ?? "Unknown error"}`);
|
|
174
|
+
const retry = await confirm("Try again?", true);
|
|
175
|
+
if (!retry) {
|
|
176
|
+
error("Wizard cancelled");
|
|
177
|
+
process.exitCode = 1;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const dbName = connectionUrl.match(/\/([^/?]+)(?:\?.*)?$/)?.[1] ?? "database";
|
|
183
|
+
const sourceName = await input("Source name", dbName);
|
|
184
|
+
newline();
|
|
185
|
+
console.log(` ${bold("Step 4 \u2014 Configure Lens")}`);
|
|
186
|
+
let selectedTable;
|
|
187
|
+
if (tables.length > 0) {
|
|
188
|
+
selectedTable = await select("Select a table:", tables);
|
|
189
|
+
} else {
|
|
190
|
+
selectedTable = await input("Table name");
|
|
191
|
+
}
|
|
192
|
+
let fields = [];
|
|
193
|
+
const schemaRes = await api.post(
|
|
194
|
+
`/v1/orgs/${orgSlug}/introspect`,
|
|
195
|
+
{ bridge, config: { url: connectionUrl }, target: selectedTable }
|
|
196
|
+
);
|
|
197
|
+
if (schemaRes.schema?.columns && schemaRes.schema.columns.length > 0) {
|
|
198
|
+
fields = schemaRes.schema.columns;
|
|
199
|
+
newline();
|
|
200
|
+
info(`Columns in "${selectedTable}":`);
|
|
201
|
+
for (const col of fields) {
|
|
202
|
+
const flags = [
|
|
203
|
+
col.primaryKey ? "PK" : "",
|
|
204
|
+
col.nullable ? "null" : ""
|
|
205
|
+
].filter(Boolean).join(", ");
|
|
206
|
+
const pad = col.name.padEnd(20);
|
|
207
|
+
const mappedType = mapDbTypeToFieldType(col.type);
|
|
208
|
+
const typePad = `${mappedType} (${col.type})`.padEnd(24);
|
|
209
|
+
console.log(` \u2714 ${pad} ${typePad} ${flags ? dim(flags) : ""}`);
|
|
210
|
+
}
|
|
211
|
+
const includeAll = await confirm("Include all columns?", true);
|
|
212
|
+
if (!includeAll) {
|
|
213
|
+
info("Include all columns for now \u2014 you can customize in the Console later.");
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
info("Could not introspect columns \u2014 you can configure fields in the Console.");
|
|
217
|
+
}
|
|
218
|
+
const lensName = await input("Lens name", selectedTable);
|
|
219
|
+
newline();
|
|
220
|
+
const enableQuery = await confirm("Enable direct query? (allows reading from your DB via API)", false);
|
|
221
|
+
if (enableQuery) {
|
|
222
|
+
info(`${dim("For production, configure JWT validation via your JWKS endpoint in lens rules.")}`);
|
|
223
|
+
}
|
|
224
|
+
newline();
|
|
225
|
+
const runIngest = await confirm("Start initial ingest?", true);
|
|
226
|
+
newline();
|
|
227
|
+
await api.post(
|
|
228
|
+
`${envPath}/sources`,
|
|
229
|
+
{ name: sourceName, bridge, config: { url: connectionUrl } }
|
|
230
|
+
);
|
|
231
|
+
success("Source created");
|
|
232
|
+
const fieldsConfig = {};
|
|
233
|
+
const searchableFieldNames = [];
|
|
234
|
+
for (const f of fields) {
|
|
235
|
+
const mappedType = mapDbTypeToFieldType(f.type);
|
|
236
|
+
const isSearchable = !f.primaryKey;
|
|
237
|
+
fieldsConfig[f.name] = {
|
|
238
|
+
type: mappedType,
|
|
239
|
+
...f.primaryKey ? { primaryKey: true } : {},
|
|
240
|
+
...isSearchable ? { searchable: true } : {}
|
|
241
|
+
};
|
|
242
|
+
if (isSearchable) searchableFieldNames.push(f.name);
|
|
243
|
+
}
|
|
244
|
+
const rules = { search: "public" };
|
|
245
|
+
if (enableQuery) rules.query = "public";
|
|
246
|
+
const lensRes = await api.post(
|
|
247
|
+
`${envPath}/lenses`,
|
|
248
|
+
{
|
|
249
|
+
name: lensName,
|
|
250
|
+
config: {
|
|
251
|
+
source: sourceName,
|
|
252
|
+
table: selectedTable,
|
|
253
|
+
fields: fieldsConfig,
|
|
254
|
+
facets: {
|
|
255
|
+
search: {
|
|
256
|
+
fields: searchableFieldNames
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
rules
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
success("Lens created");
|
|
264
|
+
if (runIngest) {
|
|
265
|
+
try {
|
|
266
|
+
await api.post(`${envPath}/lenses/${lensRes.id}/ingest`, {});
|
|
267
|
+
success("Ingest triggered");
|
|
268
|
+
} catch {
|
|
269
|
+
error("Ingest failed to start \u2014 you can trigger it manually.");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
newline();
|
|
273
|
+
console.log(` ${"\u2500".repeat(40)}`);
|
|
274
|
+
console.log(` ${bold("Your publishable key:")}`);
|
|
275
|
+
newline();
|
|
276
|
+
console.log(` ${publishableKey ?? dim("(no key found \u2014 generate one in the Console)")}`);
|
|
277
|
+
if (publishableKey) {
|
|
278
|
+
newline();
|
|
279
|
+
console.log(` ${bold("Add to your project:")}`);
|
|
280
|
+
newline();
|
|
281
|
+
console.log(` ${dim("# React (.env)")}`);
|
|
282
|
+
console.log(` REACT_APP_SEMILAYER_PK=${publishableKey}`);
|
|
283
|
+
newline();
|
|
284
|
+
console.log(` ${dim("# Vite (.env)")}`);
|
|
285
|
+
console.log(` VITE_SEMILAYER_PK=${publishableKey}`);
|
|
286
|
+
newline();
|
|
287
|
+
console.log(` ${dim("# Next.js (.env.local)")}`);
|
|
288
|
+
console.log(` NEXT_PUBLIC_SEMILAYER_PK=${publishableKey}`);
|
|
289
|
+
newline();
|
|
290
|
+
console.log(` ${dim("# Generic (.env)")}`);
|
|
291
|
+
console.log(` SEMILAYER_PK=${publishableKey}`);
|
|
292
|
+
}
|
|
293
|
+
console.log(` ${"\u2500".repeat(40)}`);
|
|
294
|
+
newline();
|
|
295
|
+
success("Setup complete!");
|
|
296
|
+
info(`View your environment: Console \u2192 ${orgSlug}/${projectSlug}/${envSlug}`);
|
|
297
|
+
} catch (err) {
|
|
298
|
+
error(err.message);
|
|
299
|
+
process.exitCode = 1;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
export {
|
|
304
|
+
wizard_default as default
|
|
305
|
+
};
|
|
306
|
+
//# sourceMappingURL=wizard-QLAR33T2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/wizard.ts"],"sourcesContent":["import { defineCommand } from 'citty'\nimport {\n mapDbTypeToFieldType,\n} from '@semilayer/core'\nimport type {\n MeResponse,\n Project,\n Environment,\n CreateProjectResponse,\n CreateEnvResponse,\n TargetSchema,\n} from '@semilayer/core'\nimport { createApiClient } from '../lib/api.js'\nimport { success, error, info, bold, dim, newline } from '../lib/output.js'\nimport { select, input, confirm, secret } from '../lib/prompt.js'\n\ninterface IntrospectResponse {\n connected: boolean\n targets?: string[]\n schema?: TargetSchema\n error?: string\n supportsIntrospection?: boolean\n}\n\nexport default defineCommand({\n meta: {\n name: 'wizard',\n description: 'Interactive setup — connect a database and get a publishable key in seconds',\n },\n args: {\n 'service-url': {\n type: 'string',\n description: 'Service API URL',\n },\n },\n async run({ args: _args }) {\n try {\n const api = await createApiClient()\n\n console.log()\n console.log(` ${bold('SemiLayer Quick Connect')}`)\n console.log(` ${'─'.repeat(23)}`)\n\n const me = await api.get<MeResponse>('/auth/me')\n\n if (me.orgs.length === 0) {\n error('No organizations found. Run `semilayer orgs create` first.')\n process.exitCode = 1\n return\n }\n\n // ── Step 1: Org ─────────────────────────────────────────────\n let orgSlug: string\n if (me.orgs.length === 1) {\n orgSlug = me.orgs[0]!.slug\n info(`Org: ${bold(orgSlug)}`)\n } else {\n newline()\n console.log(` ${bold('Step 1 — Organization')}`)\n orgSlug = await select('Select an org:', me.orgs.map((o) => o.slug))\n }\n\n // ── Step 2: Project ─────────────────────────────────────────\n newline()\n console.log(` ${bold('Step 1 — Project')}`)\n const { projects } = await api.get<{ projects: Project[] }>(\n `/v1/orgs/${orgSlug}/projects`,\n )\n\n let projectSlug: string\n let publishableKey: string | null = null\n let autoEnvSlug: string | null = null\n\n const projectChoices = [\n ...projects.map((p) => p.slug),\n '+ Create new project',\n ]\n const projectChoice = await select('Select a project:', projectChoices)\n\n if (projectChoice === '+ Create new project') {\n const name = await input('Project name', 'My Project')\n const slug = await input(\n 'Slug',\n name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),\n )\n const res = await api.post<CreateProjectResponse>(\n `/v1/orgs/${orgSlug}/projects`,\n { name, slug },\n )\n projectSlug = res.project.slug\n autoEnvSlug = 'development'\n const pk = res.keys?.find((k) => k.type === 'publishable')\n if (pk) publishableKey = pk.key\n success(`Created project ${bold(projectSlug)} with Development environment`)\n } else {\n projectSlug = projectChoice\n info(`Project: ${bold(projectSlug)}`)\n }\n\n // ── Step 3: Environment ─────────────────────────────────────\n let envSlug: string\n\n if (autoEnvSlug) {\n envSlug = autoEnvSlug\n success(`Using ${bold('development')} environment`)\n } else {\n newline()\n console.log(` ${bold('Step 2 — Environment')}`)\n const { environments } = await api.get<{ environments: Environment[] }>(\n `/v1/orgs/${orgSlug}/projects/${projectSlug}/envs`,\n )\n\n const envChoices = [\n ...environments.map((e) => e.slug),\n '+ Create new environment',\n ]\n const envChoice = await select('Select an environment:', envChoices)\n\n if (envChoice === '+ Create new environment') {\n const name = await input('Environment name', 'Staging')\n const slug = await input(\n 'Slug',\n name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),\n )\n const res = await api.post<CreateEnvResponse>(\n `/v1/orgs/${orgSlug}/projects/${projectSlug}/envs`,\n { name, slug },\n )\n envSlug = res.environment.slug\n const pk = res.keys?.find((k) => k.type === 'publishable')\n if (pk) publishableKey = pk.key\n success(`Created environment ${bold(envSlug)}`)\n } else {\n envSlug = envChoice\n info(`Environment: ${bold(envSlug)}`)\n }\n }\n\n const envPath = `/v1/orgs/${orgSlug}/projects/${projectSlug}/envs/${envSlug}`\n\n // Fetch pk if we don't have one yet (existing project + existing env)\n if (!publishableKey) {\n try {\n const keysRes = await api.get<{\n keys: Array<{ type: string; key: string }>\n }>(`${envPath}/keys?reveal=true`)\n const pk = keysRes.keys.find((k) => k.type === 'publishable')\n if (pk) publishableKey = pk.key\n } catch {\n // Will try again at the end\n }\n }\n\n // ── Optional: Auth provider ──────────────────────────────────\n newline()\n const configureAuth = await confirm('Configure end-user authentication? (JWKS from Auth0/Clerk/etc.)', false)\n if (configureAuth) {\n const jwksUrl = await input('JWKS URL')\n const authIssuer = await input('Issuer')\n const authAudience = await input('Audience (optional, press Enter to skip)')\n try {\n await api.patch(`${envPath}/auth`, {\n jwksUrl,\n issuer: authIssuer,\n ...(authAudience ? { audience: authAudience } : {}),\n })\n success('Auth provider configured')\n info(`Lens rules can now use ${bold(\"'authenticated'\")} and claim checks`)\n } catch (err) {\n error(`Failed to configure auth: ${(err as Error).message}`)\n }\n }\n\n // ── Step 4: Connection ──────────────────────────────────────\n newline()\n console.log(` ${bold('Step 3 — Connect Database')}`)\n const bridge = '@semilayer/bridge-postgres'\n info(`Bridge: ${bold('PostgreSQL')}`)\n\n let connectionUrl = ''\n let tables: string[] = []\n\n // Retry loop for connection testing\n while (true) {\n connectionUrl = await secret('Connection URL')\n if (!connectionUrl) {\n error('Connection URL is required')\n continue\n }\n\n info('Testing connection...')\n const res = await api.post<IntrospectResponse>(\n `/v1/orgs/${orgSlug}/introspect`,\n { bridge, config: { url: connectionUrl } },\n )\n\n if (res.connected) {\n tables = res.targets ?? []\n success(`Connected — ${tables.length} tables found`)\n break\n } else {\n error(`Connection failed: ${res.error ?? 'Unknown error'}`)\n const retry = await confirm('Try again?', true)\n if (!retry) {\n error('Wizard cancelled')\n process.exitCode = 1\n return\n }\n }\n }\n\n const dbName = connectionUrl.match(/\\/([^/?]+)(?:\\?.*)?$/)?.[1] ?? 'database'\n const sourceName = await input('Source name', dbName)\n\n // ── Step 5: Configure Lens ──────────────────────────────────\n newline()\n console.log(` ${bold('Step 4 — Configure Lens')}`)\n\n let selectedTable: string\n if (tables.length > 0) {\n selectedTable = await select('Select a table:', tables)\n } else {\n selectedTable = await input('Table name')\n }\n\n // Introspect columns\n let fields: Array<{ name: string; type: string; primaryKey: boolean; nullable: boolean }> = []\n\n const schemaRes = await api.post<IntrospectResponse>(\n `/v1/orgs/${orgSlug}/introspect`,\n { bridge, config: { url: connectionUrl }, target: selectedTable },\n )\n\n if (schemaRes.schema?.columns && schemaRes.schema.columns.length > 0) {\n fields = schemaRes.schema.columns\n newline()\n info(`Columns in \"${selectedTable}\":`)\n for (const col of fields) {\n const flags = [\n col.primaryKey ? 'PK' : '',\n col.nullable ? 'null' : '',\n ]\n .filter(Boolean)\n .join(', ')\n const pad = col.name.padEnd(20)\n const mappedType = mapDbTypeToFieldType(col.type)\n const typePad = `${mappedType} (${col.type})`.padEnd(24)\n console.log(` ✔ ${pad} ${typePad} ${flags ? dim(flags) : ''}`)\n }\n\n const includeAll = await confirm('Include all columns?', true)\n if (!includeAll) {\n info('Include all columns for now — you can customize in the Console later.')\n }\n } else {\n info('Could not introspect columns — you can configure fields in the Console.')\n }\n\n const lensName = await input('Lens name', selectedTable)\n\n newline()\n const enableQuery = await confirm('Enable direct query? (allows reading from your DB via API)', false)\n if (enableQuery) {\n info(`${dim('For production, configure JWT validation via your JWKS endpoint in lens rules.')}`)\n }\n\n // ── Step 6: Create resources ────────────────────────────────\n newline()\n const runIngest = await confirm('Start initial ingest?', true)\n\n newline()\n // Create source\n await api.post<{ id: string }>(\n `${envPath}/sources`,\n { name: sourceName, bridge, config: { url: connectionUrl } },\n )\n success('Source created')\n\n // Build lens config with proper field types + searchable flags\n const fieldsConfig: Record<string, { type: string; primaryKey?: boolean; searchable?: boolean }> = {}\n const searchableFieldNames: string[] = []\n\n for (const f of fields) {\n const mappedType = mapDbTypeToFieldType(f.type)\n const isSearchable = !f.primaryKey\n fieldsConfig[f.name] = {\n type: mappedType,\n ...(f.primaryKey ? { primaryKey: true } : {}),\n ...(isSearchable ? { searchable: true } : {}),\n }\n if (isSearchable) searchableFieldNames.push(f.name)\n }\n\n const rules: Record<string, string> = { search: 'public' }\n if (enableQuery) rules.query = 'public'\n\n const lensRes = await api.post<{ id: string; name: string }>(\n `${envPath}/lenses`,\n {\n name: lensName,\n config: {\n source: sourceName,\n table: selectedTable,\n fields: fieldsConfig,\n facets: {\n search: {\n fields: searchableFieldNames,\n },\n },\n rules,\n },\n },\n )\n success('Lens created')\n\n // Trigger ingest\n if (runIngest) {\n try {\n await api.post(`${envPath}/lenses/${lensRes.id}/ingest`, {})\n success('Ingest triggered')\n } catch {\n error('Ingest failed to start — you can trigger it manually.')\n }\n }\n\n // ── Step 7: Show results ────────────────────────────────────\n newline()\n console.log(` ${'─'.repeat(40)}`)\n console.log(` ${bold('Your publishable key:')}`)\n newline()\n console.log(` ${publishableKey ?? dim('(no key found — generate one in the Console)')}`)\n\n if (publishableKey) {\n newline()\n console.log(` ${bold('Add to your project:')}`)\n newline()\n console.log(` ${dim('# React (.env)')}`)\n console.log(` REACT_APP_SEMILAYER_PK=${publishableKey}`)\n newline()\n console.log(` ${dim('# Vite (.env)')}`)\n console.log(` VITE_SEMILAYER_PK=${publishableKey}`)\n newline()\n console.log(` ${dim('# Next.js (.env.local)')}`)\n console.log(` NEXT_PUBLIC_SEMILAYER_PK=${publishableKey}`)\n newline()\n console.log(` ${dim('# Generic (.env)')}`)\n console.log(` SEMILAYER_PK=${publishableKey}`)\n }\n\n console.log(` ${'─'.repeat(40)}`)\n newline()\n success('Setup complete!')\n info(`View your environment: Console → ${orgSlug}/${projectSlug}/${envSlug}`)\n } catch (err) {\n error((err as Error).message)\n process.exitCode = 1\n }\n },\n})\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@semilayer/cli",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "SemiLayer CLI — init, generate, push, status, dev",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"dev": "tsup --watch",
|
|
20
|
+
"lint": "eslint src/",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"test": "vitest run"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@semilayer/core": "workspace:*",
|
|
26
|
+
"@semilayer/codegen": "workspace:*",
|
|
27
|
+
"@semilayer/client": "workspace:*",
|
|
28
|
+
"citty": "^0.1.0",
|
|
29
|
+
"jiti": "^2.4.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.0.0",
|
|
33
|
+
"tsup": "^8.0.0",
|
|
34
|
+
"typescript": "^5.7.0",
|
|
35
|
+
"vitest": "^3.0.0"
|
|
36
|
+
},
|
|
37
|
+
"bin": {
|
|
38
|
+
"semilayer": "./dist/bin.js"
|
|
39
|
+
}
|
|
40
|
+
}
|