@getstrata/starter 0.1.12 → 1.0.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/README.md +1 -1
- package/dist/cli.js +68 -50
- package/dist/templates/package.json +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# create-strata
|
|
2
2
|
|
|
3
|
-
Interactive starter for [Strata](https://github.com/EyK-26/strata). The wizard always asks each layer. Extras (MFA, email verification, SCIM, metrics) are toggled one by one. SQLite APIs and Postgres HTML apps use the same generator.
|
|
3
|
+
Interactive starter for [Strata](https://github.com/EyK-26/strata). The wizard always asks each layer. In a terminal, lists use ↑/↓ and Enter (or a number). Extras (MFA, email verification, SCIM, metrics) are toggled one by one. SQLite APIs and Postgres HTML apps use the same generator.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
package/dist/cli.js
CHANGED
|
@@ -508,10 +508,36 @@ function layersFromFlags(flags) {
|
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
// src/prompt.ts
|
|
511
|
-
import { stdin as input, stdout as output } from "process";
|
|
512
511
|
import { createInterface } from "readline/promises";
|
|
512
|
+
import tty2 from "tty";
|
|
513
513
|
|
|
514
514
|
// src/selectPrompt.ts
|
|
515
|
+
import tty from "tty";
|
|
516
|
+
function hasRawMode(input) {
|
|
517
|
+
return Boolean(input.isTTY && typeof input.setRawMode === "function");
|
|
518
|
+
}
|
|
519
|
+
var cachedFdIo;
|
|
520
|
+
function resolveSelectIo(preferred) {
|
|
521
|
+
if (preferred) {
|
|
522
|
+
return preferred;
|
|
523
|
+
}
|
|
524
|
+
const live = { input: process.stdin, output: process.stdout };
|
|
525
|
+
if (hasRawMode(live.input)) {
|
|
526
|
+
return live;
|
|
527
|
+
}
|
|
528
|
+
if (cachedFdIo && hasRawMode(cachedFdIo.input)) {
|
|
529
|
+
return cachedFdIo;
|
|
530
|
+
}
|
|
531
|
+
if (tty.isatty(0)) {
|
|
532
|
+
cachedFdIo = {
|
|
533
|
+
input: new tty.ReadStream(0),
|
|
534
|
+
output: tty.isatty(1) ? new tty.WriteStream(1) : process.stdout
|
|
535
|
+
};
|
|
536
|
+
return cachedFdIo;
|
|
537
|
+
}
|
|
538
|
+
return live;
|
|
539
|
+
}
|
|
540
|
+
|
|
515
541
|
class PromptCancelledError extends Error {
|
|
516
542
|
constructor() {
|
|
517
543
|
super("Cancelled");
|
|
@@ -851,54 +877,51 @@ function isInteractive(flags) {
|
|
|
851
877
|
if (flags.yes || flags.noInteractive) {
|
|
852
878
|
return false;
|
|
853
879
|
}
|
|
854
|
-
return Boolean(
|
|
855
|
-
}
|
|
856
|
-
function canUseRawKeys() {
|
|
857
|
-
return Boolean(input.isTTY && typeof input.setRawMode === "function");
|
|
880
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY || tty2.isatty(0) && tty2.isatty(1));
|
|
858
881
|
}
|
|
859
|
-
function createReadlinePrompter() {
|
|
860
|
-
const
|
|
861
|
-
|
|
882
|
+
function createReadlinePrompter(io) {
|
|
883
|
+
const stdio = () => resolveSelectIo(io);
|
|
884
|
+
async function askLine(message) {
|
|
885
|
+
const current = stdio();
|
|
886
|
+
const rl = createInterface({ input: current.input, output: current.output });
|
|
887
|
+
try {
|
|
888
|
+
return (await rl.question(message)).trim();
|
|
889
|
+
} finally {
|
|
890
|
+
rl.close();
|
|
891
|
+
}
|
|
892
|
+
}
|
|
862
893
|
return {
|
|
863
894
|
async question(message, defaultValue) {
|
|
864
895
|
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
865
|
-
const answer =
|
|
896
|
+
const answer = await askLine(`${message}${suffix}: `);
|
|
866
897
|
return answer || defaultValue || "";
|
|
867
898
|
},
|
|
868
899
|
async confirm(message, defaultValue = false) {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
return await promptConfirm(message, defaultValue, io);
|
|
873
|
-
} finally {
|
|
874
|
-
input.setRawMode?.(false);
|
|
875
|
-
rl.resume();
|
|
876
|
-
}
|
|
900
|
+
const current = stdio();
|
|
901
|
+
if (hasRawMode(current.input)) {
|
|
902
|
+
return promptConfirm(message, defaultValue, current);
|
|
877
903
|
}
|
|
878
904
|
const hint = defaultValue ? "Y/n" : "y/N";
|
|
879
|
-
const answer = (await
|
|
905
|
+
const answer = (await askLine(`${message} (${hint}): `)).toLowerCase();
|
|
880
906
|
if (!answer) {
|
|
881
907
|
return defaultValue;
|
|
882
908
|
}
|
|
883
909
|
return answer === "y" || answer === "yes";
|
|
884
910
|
},
|
|
885
911
|
async select(message, choices, defaultValue) {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
return await promptSelect(message, choices, defaultValue, io);
|
|
890
|
-
} finally {
|
|
891
|
-
input.setRawMode?.(false);
|
|
892
|
-
rl.resume();
|
|
893
|
-
}
|
|
912
|
+
const current = stdio();
|
|
913
|
+
if (hasRawMode(current.input)) {
|
|
914
|
+
return promptSelect(message, choices, defaultValue, current);
|
|
894
915
|
}
|
|
895
|
-
|
|
916
|
+
current.output.write(`${message}
|
|
917
|
+
`);
|
|
896
918
|
for (const [index, choice] of choices.entries()) {
|
|
897
919
|
const marker = choice.value === defaultValue ? "*" : " ";
|
|
898
|
-
|
|
920
|
+
current.output.write(` ${index + 1}) ${marker} ${choice.label}
|
|
921
|
+
`);
|
|
899
922
|
}
|
|
900
923
|
const defaultIndex = choices.findIndex((choice) => choice.value === defaultValue) + 1;
|
|
901
|
-
const answer =
|
|
924
|
+
const answer = await askLine(`Choose [${defaultIndex}]: `);
|
|
902
925
|
if (!answer) {
|
|
903
926
|
return defaultValue;
|
|
904
927
|
}
|
|
@@ -911,20 +934,16 @@ function createReadlinePrompter() {
|
|
|
911
934
|
return match?.value ?? defaultValue;
|
|
912
935
|
},
|
|
913
936
|
async multiSelect(message, choices) {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
return await promptMultiSelect(message, choices, io);
|
|
918
|
-
} finally {
|
|
919
|
-
input.setRawMode?.(false);
|
|
920
|
-
rl.resume();
|
|
921
|
-
}
|
|
937
|
+
const current = stdio();
|
|
938
|
+
if (hasRawMode(current.input)) {
|
|
939
|
+
return promptMultiSelect(message, choices, current);
|
|
922
940
|
}
|
|
923
941
|
const enabled = new Set(choices.filter((choice) => choice.enabled).map((choice) => choice.value));
|
|
924
|
-
|
|
942
|
+
current.output.write(`${message} (yes/no each)
|
|
943
|
+
`);
|
|
925
944
|
for (const choice of choices) {
|
|
926
945
|
const hint = enabled.has(choice.value) ? "Y/n" : "y/N";
|
|
927
|
-
const answer = (await
|
|
946
|
+
const answer = (await askLine(` ${choice.label} (${hint}): `)).toLowerCase();
|
|
928
947
|
if (!answer) {
|
|
929
948
|
continue;
|
|
930
949
|
}
|
|
@@ -937,7 +956,7 @@ function createReadlinePrompter() {
|
|
|
937
956
|
return [...enabled];
|
|
938
957
|
},
|
|
939
958
|
close() {
|
|
940
|
-
|
|
959
|
+
return;
|
|
941
960
|
}
|
|
942
961
|
};
|
|
943
962
|
}
|
|
@@ -2415,9 +2434,9 @@ function renderPackageJson(projectName, options = {}) {
|
|
|
2415
2434
|
"@getstrata/cli": "workspace:*",
|
|
2416
2435
|
"@getstrata/core": "workspace:*"
|
|
2417
2436
|
} : {
|
|
2418
|
-
"@getstrata/bootstrap": "^0.
|
|
2419
|
-
"@getstrata/cli": "^0.
|
|
2420
|
-
"@getstrata/core": "^0.
|
|
2437
|
+
"@getstrata/bootstrap": "^1.0.0",
|
|
2438
|
+
"@getstrata/cli": "^1.0.0",
|
|
2439
|
+
"@getstrata/core": "^1.0.0"
|
|
2421
2440
|
};
|
|
2422
2441
|
if (options.layers?.database === "mysql") {
|
|
2423
2442
|
coreDeps.mysql2 = "^3.24.3";
|
|
@@ -2852,13 +2871,13 @@ function renderMigrateTs(layers) {
|
|
|
2852
2871
|
}
|
|
2853
2872
|
const list = statements.map((sql) => ` \`${sql}\`,`).join(`
|
|
2854
2873
|
`);
|
|
2855
|
-
const
|
|
2856
|
-
const notePlaceholder =
|
|
2874
|
+
const ph = layers.database === "postgres";
|
|
2875
|
+
const notePlaceholder = ph ? "$1" : "?";
|
|
2857
2876
|
const verifyOn = layers.extras.emailVerification && authNeedsUsers(layers.auth);
|
|
2858
2877
|
const userColumns = verifyOn ? "name, email, password, is_admin, email_verified_at" : "name, email, password, is_admin";
|
|
2859
|
-
const userPlaceholders = verifyOn ?
|
|
2860
|
-
const adminFlag =
|
|
2861
|
-
const adminTrue =
|
|
2878
|
+
const userPlaceholders = verifyOn ? ph ? "$1, $2, $3, $4, $5), ($6, $7, $8, $9, $10" : "?, ?, ?, ?, ?), (?, ?, ?, ?, ?" : ph ? "$1, $2, $3, $4), ($5, $6, $7, $8" : "?, ?, ?, ?), (?, ?, ?, ?";
|
|
2879
|
+
const adminFlag = ph ? "false" : "0";
|
|
2880
|
+
const adminTrue = ph ? "true" : "1";
|
|
2862
2881
|
const verifiedNow = "new Date().toISOString()";
|
|
2863
2882
|
const userValues = verifyOn ? `["Demo User", "demo@example.com", password, ${adminFlag}, ${verifiedNow}, "Admin User", "admin@example.test", password, ${adminTrue}, ${verifiedNow}]` : `["Demo User", "demo@example.com", password, ${adminFlag}, "Admin User", "admin@example.test", password, ${adminTrue}]`;
|
|
2864
2883
|
const seedTenant = tenancyOn ? `
|
|
@@ -2867,7 +2886,7 @@ function renderMigrateTs(layers) {
|
|
|
2867
2886
|
);
|
|
2868
2887
|
if (Number(tenantCount) === 0) {
|
|
2869
2888
|
await sql.unsafe(
|
|
2870
|
-
"INSERT INTO tenant (slug, plan, region) VALUES (${
|
|
2889
|
+
"INSERT INTO tenant (slug, plan, region) VALUES (${ph ? "$1, $2, $3" : "?, ?, ?"})",
|
|
2871
2890
|
["default", "enterprise", "eu"],
|
|
2872
2891
|
);
|
|
2873
2892
|
}` : "";
|
|
@@ -3166,7 +3185,6 @@ function adminCandidateUrls(url: string): string[] {
|
|
|
3166
3185
|
names.push(current);
|
|
3167
3186
|
}
|
|
3168
3187
|
} catch {
|
|
3169
|
-
// keep the built-in admin databases
|
|
3170
3188
|
}
|
|
3171
3189
|
return names.map((name) => {
|
|
3172
3190
|
const admin = new URL(url);
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"check": "tsc --noEmit"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@getstrata/bootstrap": "^0.
|
|
15
|
-
"@getstrata/cli": "^0.
|
|
16
|
-
"@getstrata/core": "^0.
|
|
14
|
+
"@getstrata/bootstrap": "^1.0.0",
|
|
15
|
+
"@getstrata/cli": "^1.0.0",
|
|
16
|
+
"@getstrata/core": "^1.0.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/bun": "^1.4.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getstrata/starter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Interactive create-strata wizard. Choose each layer; one database engine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,6 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"engines": {
|
|
27
|
-
"bun": ">=1.
|
|
27
|
+
"bun": ">=1.4.0"
|
|
28
28
|
}
|
|
29
29
|
}
|