@stratum-hq/create 0.4.0 → 0.4.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/CHANGELOG.md +17 -0
- package/README.md +2 -1
- package/dist/bin.js +3 -0
- package/dist/index.js +612 -96
- package/package.json +3 -3
- package/src/bin.ts +7 -0
- package/src/generators/db-setup.ts +91 -26
- package/src/generators/init-sql.ts +4 -1
- package/src/generators/package-json.ts +31 -12
- package/src/generators/readme.ts +2 -10
- package/src/generators/tsconfig.ts +48 -0
- package/src/index.ts +11 -30
- package/src/preset-project.ts +2 -25
- package/src/stratum-versions.ts +20 -0
- package/turbo.json +19 -0
package/dist/index.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
// src/index.ts
|
|
4
2
|
import * as fs2 from "fs";
|
|
5
3
|
import * as path2 from "path";
|
|
6
4
|
import crypto2 from "node:crypto";
|
|
7
5
|
import { execSync as execSync2 } from "child_process";
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
9
6
|
|
|
10
7
|
// src/matrix.ts
|
|
11
8
|
var POSTGRES_STRATEGIES = ["rls", "schema", "database"];
|
|
@@ -224,8 +221,11 @@ function generatePostgresInit(projectName, dbName, strategy) {
|
|
|
224
221
|
-- ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
|
|
225
222
|
-- ALTER TABLE your_table FORCE ROW LEVEL SECURITY;
|
|
226
223
|
-- CREATE POLICY tenant_isolation ON your_table
|
|
227
|
-
-- USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
|
|
224
|
+
-- USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid);
|
|
228
225
|
--
|
|
226
|
+
-- A pooled connection reads the setting as '' after a tenant transaction ends,
|
|
227
|
+
-- or as NULL before the first one. NULLIF makes both return no rows; a bare
|
|
228
|
+
-- ::uuid cast of '' raises an error.
|
|
229
229
|
-- FORCE makes the policy apply to the table owner too; without it, a table
|
|
230
230
|
-- created by the application role is not isolated for that role.
|
|
231
231
|
-- The Stratum db-adapters package sets app.current_tenant_id automatically.
|
|
@@ -446,59 +446,106 @@ export default config;
|
|
|
446
446
|
},
|
|
447
447
|
{
|
|
448
448
|
filename: "src/stratum-knex.ts",
|
|
449
|
-
content:
|
|
449
|
+
content: preset.database === "postgres" ? KNEX_POSTGRES_SCOPE : knexColumnScope(preset.database)
|
|
450
|
+
}
|
|
451
|
+
];
|
|
452
|
+
}
|
|
453
|
+
var KNEX_POSTGRES_SCOPE = `// Knex with Stratum tenant-scoped queries
|
|
454
|
+
import createKnex, { type Knex } from "knex";
|
|
455
|
+
import config from "../knexfile.js";
|
|
456
|
+
|
|
457
|
+
const knex = createKnex(config);
|
|
458
|
+
|
|
459
|
+
// Run fn in a transaction that sets app.current_tenant_id, the setting that
|
|
460
|
+
// the RLS policies read. The setting ends with the transaction, so run every
|
|
461
|
+
// tenant query through trx, not through knex.
|
|
462
|
+
export async function withTenantScope<T>(
|
|
463
|
+
tenantId: string,
|
|
464
|
+
fn: (trx: Knex.Transaction) => Promise<T>,
|
|
465
|
+
): Promise<T> {
|
|
466
|
+
return knex.transaction(async (trx) => {
|
|
467
|
+
await trx.raw("SELECT set_config('app.current_tenant_id', ?, true)", [tenantId]);
|
|
468
|
+
return fn(trx);
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Usage:
|
|
473
|
+
// const orders = await withTenantScope(currentTenantId, (trx) => trx("orders").select());
|
|
474
|
+
|
|
475
|
+
export { knex };
|
|
476
|
+
`;
|
|
477
|
+
function knexColumnScope(database) {
|
|
478
|
+
return `// Knex with Stratum tenant-scoped queries
|
|
450
479
|
import Knex from "knex";
|
|
451
480
|
import config from "../knexfile.js";
|
|
452
481
|
|
|
453
482
|
const knex = Knex(config);
|
|
454
483
|
|
|
455
484
|
// Create a tenant-scoped query builder.
|
|
456
|
-
// For RLS strategy, set the session variable before queries.
|
|
457
485
|
export async function withTenantScope(tenantId: string, fn: (db: typeof knex) => Promise<void>) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
await fn(knex);
|
|
461
|
-
} finally {
|
|
462
|
-
${preset.database === "postgres" ? ` await knex.raw("RESET app.current_tenant");` : ` // Scope cleanup not needed for column-based isolation`}
|
|
463
|
-
}
|
|
486
|
+
// For ${database}, scope queries by tenant_id column
|
|
487
|
+
await fn(knex);
|
|
464
488
|
}
|
|
465
489
|
|
|
466
490
|
export { knex };
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
];
|
|
491
|
+
`;
|
|
470
492
|
}
|
|
471
|
-
function generateMongooseSetup(
|
|
493
|
+
function generateMongooseSetup(preset) {
|
|
494
|
+
const tenantAccess = preset.strategy === "collection" ? `// Return the tenant's model for a base collection. The tenant's documents are
|
|
495
|
+
// in the collection {baseCollection}_{tenantSlug}, the name that
|
|
496
|
+
// MongoCollectionAdapter from @stratum-hq/mongodb uses.
|
|
497
|
+
export function getTenantModel<T>(
|
|
498
|
+
baseCollection: string,
|
|
499
|
+
schema: mongoose.Schema<T>,
|
|
500
|
+
tenantSlug: string,
|
|
501
|
+
) {
|
|
502
|
+
const name = \`\${baseCollection}_\${assertSlug(tenantSlug)}\`;
|
|
503
|
+
return mainConnection.models[name] ?? mainConnection.model(name, schema, name);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Usage:
|
|
507
|
+
// const Order = getTenantModel("orders", OrderSchema, "tenant_abc");
|
|
508
|
+
// const orders = await Order.find();` : `// Return a connection to the tenant's own database. The database name is
|
|
509
|
+
// stratum_tenant_{tenantSlug}, the name that MongoDatabaseAdapter from
|
|
510
|
+
// @stratum-hq/mongodb uses. useCache returns the same connection on each call.
|
|
511
|
+
export function getTenantConnection(tenantSlug: string) {
|
|
512
|
+
return mainConnection.useDb(\`stratum_tenant_\${assertSlug(tenantSlug)}\`, { useCache: true });
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Usage:
|
|
516
|
+
// const conn = getTenantConnection("tenant_abc");
|
|
517
|
+
// const Order = conn.model("Order", OrderSchema);
|
|
518
|
+
// const orders = await Order.find();`;
|
|
472
519
|
return [
|
|
473
520
|
{
|
|
474
521
|
filename: "src/stratum-mongoose.ts",
|
|
475
522
|
content: `// Mongoose with Stratum multi-tenant support
|
|
476
523
|
import mongoose from "mongoose";
|
|
477
|
-
import { createTenantConnection } from "@stratum-hq/mongodb";
|
|
478
524
|
|
|
479
525
|
// Main connection (used for tenant metadata)
|
|
480
526
|
const mainConnection = mongoose.createConnection(
|
|
481
527
|
process.env.MONGODB_URI || "mongodb://localhost:27017/main",
|
|
482
528
|
);
|
|
483
529
|
|
|
484
|
-
//
|
|
485
|
-
//
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
530
|
+
// A tenant slug becomes part of a MongoDB name, so it must match the Stratum
|
|
531
|
+
// slug rule: a lowercase letter, then lowercase letters, digits or underscores.
|
|
532
|
+
const SLUG_PATTERN = /^[a-z][a-z0-9_]{0,62}$/;
|
|
533
|
+
|
|
534
|
+
function assertSlug(tenantSlug: string): string {
|
|
535
|
+
if (!SLUG_PATTERN.test(tenantSlug)) {
|
|
536
|
+
throw new Error(\`Invalid tenant slug: "\${tenantSlug}"\`);
|
|
537
|
+
}
|
|
538
|
+
return tenantSlug;
|
|
489
539
|
}
|
|
490
540
|
|
|
541
|
+
${tenantAccess}
|
|
542
|
+
|
|
491
543
|
// Define schemas that work across tenant connections
|
|
492
544
|
export const TenantSchema = new mongoose.Schema({
|
|
493
545
|
name: { type: String, required: true },
|
|
494
546
|
createdAt: { type: Date, default: Date.now },
|
|
495
547
|
});
|
|
496
548
|
|
|
497
|
-
// Usage:
|
|
498
|
-
// const conn = getTenantConnection("tenant-abc");
|
|
499
|
-
// const Order = conn.model("Order", OrderSchema);
|
|
500
|
-
// const orders = await Order.find();
|
|
501
|
-
|
|
502
549
|
export { mainConnection };
|
|
503
550
|
`
|
|
504
551
|
}
|
|
@@ -517,7 +564,11 @@ const pool = mysql.createPool({
|
|
|
517
564
|
});
|
|
518
565
|
|
|
519
566
|
// Execute a query scoped to a tenant by filtering on tenant_id
|
|
520
|
-
export async function tenantQuery(
|
|
567
|
+
export async function tenantQuery(
|
|
568
|
+
tenantId: string,
|
|
569
|
+
sql: string,
|
|
570
|
+
params: (string | number | bigint | boolean | Date | null)[] = [],
|
|
571
|
+
) {
|
|
521
572
|
const [rows] = await pool.execute(sql, [...params, tenantId]);
|
|
522
573
|
return rows;
|
|
523
574
|
}
|
|
@@ -542,9 +593,10 @@ const pool = new Pool({
|
|
|
542
593
|
});
|
|
543
594
|
|
|
544
595
|
// Create a tenant-scoped pool.
|
|
545
|
-
//
|
|
596
|
+
// Each query runs in a transaction that sets app.current_tenant_id, the
|
|
597
|
+
// setting that the RLS policies read.
|
|
546
598
|
export function getTenantPool(tenantId: string) {
|
|
547
|
-
return createTenantPool(pool, tenantId);
|
|
599
|
+
return createTenantPool(pool, () => tenantId);
|
|
548
600
|
}
|
|
549
601
|
|
|
550
602
|
// Usage:
|
|
@@ -822,16 +874,516 @@ console.log("Add tenant resolution logic for your use case");
|
|
|
822
874
|
];
|
|
823
875
|
}
|
|
824
876
|
|
|
877
|
+
// src/generators/tsconfig.ts
|
|
878
|
+
function rootSources(preset) {
|
|
879
|
+
return preset.orm === "knex" ? ["knexfile.ts"] : [];
|
|
880
|
+
}
|
|
881
|
+
function generateTsconfig(framework, extraSources = []) {
|
|
882
|
+
const rootDir = extraSources.length > 0 ? "." : "src";
|
|
883
|
+
return JSON.stringify(
|
|
884
|
+
{
|
|
885
|
+
compilerOptions: {
|
|
886
|
+
target: "ESNext",
|
|
887
|
+
module: "NodeNext",
|
|
888
|
+
moduleResolution: "NodeNext",
|
|
889
|
+
strict: true,
|
|
890
|
+
outDir: "dist",
|
|
891
|
+
rootDir,
|
|
892
|
+
declaration: true,
|
|
893
|
+
skipLibCheck: true,
|
|
894
|
+
esModuleInterop: true,
|
|
895
|
+
...framework === "nestjs" ? { experimentalDecorators: true, emitDecoratorMetadata: true } : {},
|
|
896
|
+
...framework === "nextjs" ? { jsx: "preserve", plugins: [{ name: "next" }] } : {}
|
|
897
|
+
},
|
|
898
|
+
include: ["src", ...extraSources],
|
|
899
|
+
exclude: ["node_modules", "dist"]
|
|
900
|
+
},
|
|
901
|
+
null,
|
|
902
|
+
2
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// ../hono/package.json
|
|
907
|
+
var package_default = {
|
|
908
|
+
name: "@stratum-hq/hono",
|
|
909
|
+
version: "1.1.0",
|
|
910
|
+
description: "Stratum Hono integration \u2014 tenant extraction middleware with ALS context",
|
|
911
|
+
keywords: [
|
|
912
|
+
"multi-tenancy",
|
|
913
|
+
"multi-tenant",
|
|
914
|
+
"saas",
|
|
915
|
+
"tenant",
|
|
916
|
+
"hono",
|
|
917
|
+
"middleware",
|
|
918
|
+
"typescript",
|
|
919
|
+
"stratum"
|
|
920
|
+
],
|
|
921
|
+
type: "module",
|
|
922
|
+
main: "./dist/index.js",
|
|
923
|
+
types: "./dist/index.d.ts",
|
|
924
|
+
exports: {
|
|
925
|
+
".": {
|
|
926
|
+
types: "./dist/index.d.ts",
|
|
927
|
+
default: "./dist/index.js"
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
files: [
|
|
931
|
+
"dist",
|
|
932
|
+
"README.md",
|
|
933
|
+
"LICENSE"
|
|
934
|
+
],
|
|
935
|
+
scripts: {
|
|
936
|
+
build: "tsc",
|
|
937
|
+
test: "vitest run",
|
|
938
|
+
typecheck: "tsc --noEmit",
|
|
939
|
+
lint: "eslint .",
|
|
940
|
+
clean: "rm -rf dist"
|
|
941
|
+
},
|
|
942
|
+
publishConfig: {
|
|
943
|
+
access: "public",
|
|
944
|
+
registry: "https://registry.npmjs.org/"
|
|
945
|
+
},
|
|
946
|
+
peerDependencies: {
|
|
947
|
+
"@stratum-hq/core": "^1.0.0",
|
|
948
|
+
"@stratum-hq/sdk": "^1.2.0",
|
|
949
|
+
hono: ">=4.0.0"
|
|
950
|
+
},
|
|
951
|
+
devDependencies: {
|
|
952
|
+
"@stratum-hq/core": "*",
|
|
953
|
+
"@stratum-hq/sdk": "*",
|
|
954
|
+
hono: "^4.13.9",
|
|
955
|
+
typescript: "^5.4.0",
|
|
956
|
+
vitest: "^4.1.11"
|
|
957
|
+
},
|
|
958
|
+
license: "MIT",
|
|
959
|
+
author: "Christian Crank",
|
|
960
|
+
homepage: "https://github.com/stratum-hq/Stratum/tree/main/packages/hono#readme",
|
|
961
|
+
bugs: "https://github.com/stratum-hq/Stratum/issues",
|
|
962
|
+
engines: {
|
|
963
|
+
node: ">=20.0.0"
|
|
964
|
+
},
|
|
965
|
+
repository: {
|
|
966
|
+
type: "git",
|
|
967
|
+
url: "https://github.com/stratum-hq/Stratum.git",
|
|
968
|
+
directory: "packages/hono"
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
// ../db-adapters/package.json
|
|
973
|
+
var package_default2 = {
|
|
974
|
+
name: "@stratum-hq/db-adapters",
|
|
975
|
+
version: "1.2.0",
|
|
976
|
+
description: "Stratum PostgreSQL adapters for tenant-scoped query isolation",
|
|
977
|
+
keywords: [
|
|
978
|
+
"multi-tenancy",
|
|
979
|
+
"multi-tenant",
|
|
980
|
+
"saas",
|
|
981
|
+
"tenant",
|
|
982
|
+
"tenant-isolation",
|
|
983
|
+
"postgresql",
|
|
984
|
+
"postgres",
|
|
985
|
+
"row-level-security",
|
|
986
|
+
"rls",
|
|
987
|
+
"schema-isolation",
|
|
988
|
+
"database",
|
|
989
|
+
"stratum"
|
|
990
|
+
],
|
|
991
|
+
main: "./dist/index.js",
|
|
992
|
+
types: "./dist/index.d.ts",
|
|
993
|
+
exports: {
|
|
994
|
+
".": {
|
|
995
|
+
types: "./dist/index.d.ts",
|
|
996
|
+
default: "./dist/index.js"
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
files: [
|
|
1000
|
+
"dist",
|
|
1001
|
+
"README.md",
|
|
1002
|
+
"LICENSE"
|
|
1003
|
+
],
|
|
1004
|
+
scripts: {
|
|
1005
|
+
build: "tsc",
|
|
1006
|
+
test: "vitest run",
|
|
1007
|
+
typecheck: "tsc --noEmit",
|
|
1008
|
+
lint: "eslint .",
|
|
1009
|
+
clean: "rm -rf dist"
|
|
1010
|
+
},
|
|
1011
|
+
publishConfig: {
|
|
1012
|
+
access: "public",
|
|
1013
|
+
registry: "https://registry.npmjs.org/"
|
|
1014
|
+
},
|
|
1015
|
+
dependencies: {
|
|
1016
|
+
"@stratum-hq/core": "^1.4.0",
|
|
1017
|
+
pg: "^8.11.0"
|
|
1018
|
+
},
|
|
1019
|
+
peerDependencies: {
|
|
1020
|
+
"drizzle-orm": ">=0.29.0"
|
|
1021
|
+
},
|
|
1022
|
+
peerDependenciesMeta: {
|
|
1023
|
+
"drizzle-orm": {
|
|
1024
|
+
optional: true
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
devDependencies: {
|
|
1028
|
+
"@prisma/client": "^5.10.0",
|
|
1029
|
+
"@types/pg": "^8.11.0",
|
|
1030
|
+
"drizzle-orm": "^0.45.3",
|
|
1031
|
+
typescript: "^5.4.0",
|
|
1032
|
+
vitest: "^4.1.11"
|
|
1033
|
+
},
|
|
1034
|
+
license: "MIT",
|
|
1035
|
+
author: "Christian Crank",
|
|
1036
|
+
homepage: "https://github.com/stratum-hq/Stratum/tree/main/packages/db-adapters#readme",
|
|
1037
|
+
bugs: "https://github.com/stratum-hq/Stratum/issues",
|
|
1038
|
+
engines: {
|
|
1039
|
+
node: ">=20.0.0"
|
|
1040
|
+
},
|
|
1041
|
+
repository: {
|
|
1042
|
+
type: "git",
|
|
1043
|
+
url: "https://github.com/stratum-hq/Stratum.git",
|
|
1044
|
+
directory: "packages/db-adapters"
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
// ../lib/package.json
|
|
1049
|
+
var package_default3 = {
|
|
1050
|
+
name: "@stratum-hq/lib",
|
|
1051
|
+
version: "1.4.0",
|
|
1052
|
+
description: "Stratum tenant management library - framework-agnostic business logic",
|
|
1053
|
+
keywords: [
|
|
1054
|
+
"multi-tenancy",
|
|
1055
|
+
"multi-tenant",
|
|
1056
|
+
"saas",
|
|
1057
|
+
"b2b",
|
|
1058
|
+
"tenant",
|
|
1059
|
+
"tenant-management",
|
|
1060
|
+
"tenant-isolation",
|
|
1061
|
+
"node",
|
|
1062
|
+
"typescript",
|
|
1063
|
+
"postgresql",
|
|
1064
|
+
"row-level-security",
|
|
1065
|
+
"rls",
|
|
1066
|
+
"schema-isolation",
|
|
1067
|
+
"config-inheritance",
|
|
1068
|
+
"gdpr",
|
|
1069
|
+
"stratum"
|
|
1070
|
+
],
|
|
1071
|
+
main: "./dist/index.js",
|
|
1072
|
+
types: "./dist/index.d.ts",
|
|
1073
|
+
exports: {
|
|
1074
|
+
".": {
|
|
1075
|
+
types: "./dist/index.d.ts",
|
|
1076
|
+
default: "./dist/index.js"
|
|
1077
|
+
}
|
|
1078
|
+
},
|
|
1079
|
+
files: [
|
|
1080
|
+
"dist",
|
|
1081
|
+
"README.md",
|
|
1082
|
+
"LICENSE"
|
|
1083
|
+
],
|
|
1084
|
+
scripts: {
|
|
1085
|
+
build: "tsc && node ../../scripts/copy-dir.mjs src/migrations dist/migrations",
|
|
1086
|
+
test: "vitest run",
|
|
1087
|
+
typecheck: "tsc --noEmit",
|
|
1088
|
+
lint: "eslint .",
|
|
1089
|
+
clean: "rm -rf dist"
|
|
1090
|
+
},
|
|
1091
|
+
publishConfig: {
|
|
1092
|
+
access: "public",
|
|
1093
|
+
registry: "https://registry.npmjs.org/"
|
|
1094
|
+
},
|
|
1095
|
+
dependencies: {
|
|
1096
|
+
"@opentelemetry/api": "^1.9.0",
|
|
1097
|
+
"@stratum-hq/core": "^1.4.0",
|
|
1098
|
+
"@stratum-hq/db-adapters": "^1.2.0",
|
|
1099
|
+
"@stratum-hq/sdk": "^1.2.0",
|
|
1100
|
+
pg: "^8.11.0"
|
|
1101
|
+
},
|
|
1102
|
+
peerDependencies: {
|
|
1103
|
+
"@opentelemetry/api": "^1.0.0"
|
|
1104
|
+
},
|
|
1105
|
+
peerDependenciesMeta: {
|
|
1106
|
+
"@opentelemetry/api": {
|
|
1107
|
+
optional: true
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
devDependencies: {
|
|
1111
|
+
"@types/pg": "^8.11.0",
|
|
1112
|
+
typescript: "^5.4.0",
|
|
1113
|
+
vitest: "^4.1.11"
|
|
1114
|
+
},
|
|
1115
|
+
license: "MIT",
|
|
1116
|
+
author: "Christian Crank",
|
|
1117
|
+
homepage: "https://github.com/stratum-hq/Stratum/tree/main/packages/lib#readme",
|
|
1118
|
+
bugs: "https://github.com/stratum-hq/Stratum/issues",
|
|
1119
|
+
engines: {
|
|
1120
|
+
node: ">=20.0.0"
|
|
1121
|
+
},
|
|
1122
|
+
repository: {
|
|
1123
|
+
type: "git",
|
|
1124
|
+
url: "https://github.com/stratum-hq/Stratum.git",
|
|
1125
|
+
directory: "packages/lib"
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
// ../mongodb/package.json
|
|
1130
|
+
var package_default4 = {
|
|
1131
|
+
name: "@stratum-hq/mongodb",
|
|
1132
|
+
version: "0.4.0",
|
|
1133
|
+
description: "MongoDB tenant isolation for Stratum \u2014 shared collection, collection-per-tenant, and database-per-tenant strategies",
|
|
1134
|
+
keywords: [
|
|
1135
|
+
"multi-tenancy",
|
|
1136
|
+
"multi-tenant",
|
|
1137
|
+
"saas",
|
|
1138
|
+
"tenant",
|
|
1139
|
+
"tenant-isolation",
|
|
1140
|
+
"mongodb",
|
|
1141
|
+
"mongoose",
|
|
1142
|
+
"database",
|
|
1143
|
+
"stratum"
|
|
1144
|
+
],
|
|
1145
|
+
main: "./dist/index.js",
|
|
1146
|
+
types: "./dist/index.d.ts",
|
|
1147
|
+
exports: {
|
|
1148
|
+
".": {
|
|
1149
|
+
types: "./dist/index.d.ts",
|
|
1150
|
+
default: "./dist/index.js"
|
|
1151
|
+
}
|
|
1152
|
+
},
|
|
1153
|
+
files: [
|
|
1154
|
+
"dist",
|
|
1155
|
+
"README.md",
|
|
1156
|
+
"LICENSE"
|
|
1157
|
+
],
|
|
1158
|
+
scripts: {
|
|
1159
|
+
build: "tsc",
|
|
1160
|
+
test: "vitest run",
|
|
1161
|
+
"test:watch": "vitest",
|
|
1162
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
1163
|
+
typecheck: "tsc --noEmit",
|
|
1164
|
+
lint: "eslint .",
|
|
1165
|
+
clean: "rm -rf dist"
|
|
1166
|
+
},
|
|
1167
|
+
dependencies: {
|
|
1168
|
+
"@stratum-hq/core": "^1.4.0",
|
|
1169
|
+
"@stratum-hq/sdk": "^1.2.0"
|
|
1170
|
+
},
|
|
1171
|
+
peerDependencies: {
|
|
1172
|
+
mongodb: "^6.0.0 || ^7.0.0"
|
|
1173
|
+
},
|
|
1174
|
+
peerDependenciesMeta: {
|
|
1175
|
+
mongoose: {
|
|
1176
|
+
optional: true
|
|
1177
|
+
}
|
|
1178
|
+
},
|
|
1179
|
+
devDependencies: {
|
|
1180
|
+
"@stratum-hq/test-utils": "*",
|
|
1181
|
+
mongodb: "^7.6.0",
|
|
1182
|
+
mongoose: "^8.0.0",
|
|
1183
|
+
typescript: "^5.9.0",
|
|
1184
|
+
vitest: "^4.1.11"
|
|
1185
|
+
},
|
|
1186
|
+
publishConfig: {
|
|
1187
|
+
access: "public"
|
|
1188
|
+
},
|
|
1189
|
+
license: "MIT",
|
|
1190
|
+
author: "Christian Crank",
|
|
1191
|
+
homepage: "https://github.com/stratum-hq/Stratum/tree/main/packages/mongodb#readme",
|
|
1192
|
+
bugs: "https://github.com/stratum-hq/Stratum/issues",
|
|
1193
|
+
engines: {
|
|
1194
|
+
node: ">=20.0.0"
|
|
1195
|
+
},
|
|
1196
|
+
repository: {
|
|
1197
|
+
type: "git",
|
|
1198
|
+
url: "https://github.com/stratum-hq/Stratum.git",
|
|
1199
|
+
directory: "packages/mongodb"
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
// ../mysql/package.json
|
|
1204
|
+
var package_default5 = {
|
|
1205
|
+
name: "@stratum-hq/mysql",
|
|
1206
|
+
version: "0.4.0",
|
|
1207
|
+
description: "MySQL tenant isolation for Stratum \u2014 shared table, table-per-tenant, and database-per-tenant strategies",
|
|
1208
|
+
keywords: [
|
|
1209
|
+
"multi-tenancy",
|
|
1210
|
+
"multi-tenant",
|
|
1211
|
+
"saas",
|
|
1212
|
+
"tenant",
|
|
1213
|
+
"tenant-isolation",
|
|
1214
|
+
"mysql",
|
|
1215
|
+
"database",
|
|
1216
|
+
"typeorm",
|
|
1217
|
+
"knex",
|
|
1218
|
+
"sequelize",
|
|
1219
|
+
"stratum"
|
|
1220
|
+
],
|
|
1221
|
+
main: "./dist/index.js",
|
|
1222
|
+
types: "./dist/index.d.ts",
|
|
1223
|
+
exports: {
|
|
1224
|
+
".": {
|
|
1225
|
+
types: "./dist/index.d.ts",
|
|
1226
|
+
default: "./dist/index.js"
|
|
1227
|
+
}
|
|
1228
|
+
},
|
|
1229
|
+
files: [
|
|
1230
|
+
"dist",
|
|
1231
|
+
"README.md",
|
|
1232
|
+
"LICENSE"
|
|
1233
|
+
],
|
|
1234
|
+
scripts: {
|
|
1235
|
+
build: "tsc",
|
|
1236
|
+
test: "vitest run",
|
|
1237
|
+
"test:watch": "vitest",
|
|
1238
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
1239
|
+
typecheck: "tsc --noEmit",
|
|
1240
|
+
lint: "eslint .",
|
|
1241
|
+
clean: "rm -rf dist"
|
|
1242
|
+
},
|
|
1243
|
+
dependencies: {
|
|
1244
|
+
"@stratum-hq/core": "^1.4.0",
|
|
1245
|
+
"@stratum-hq/sdk": "^1.2.0"
|
|
1246
|
+
},
|
|
1247
|
+
peerDependencies: {
|
|
1248
|
+
mysql2: "^3.0.0",
|
|
1249
|
+
knex: ">=3.0.0",
|
|
1250
|
+
typeorm: "^1.0.0",
|
|
1251
|
+
sequelize: "^6.0.0"
|
|
1252
|
+
},
|
|
1253
|
+
peerDependenciesMeta: {
|
|
1254
|
+
typeorm: {
|
|
1255
|
+
optional: true
|
|
1256
|
+
},
|
|
1257
|
+
knex: {
|
|
1258
|
+
optional: true
|
|
1259
|
+
},
|
|
1260
|
+
sequelize: {
|
|
1261
|
+
optional: true
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
devDependencies: {
|
|
1265
|
+
knex: "^3.3.0",
|
|
1266
|
+
mysql2: "^3.24.4",
|
|
1267
|
+
typeorm: "^1.1.1",
|
|
1268
|
+
typescript: "^5.9.0",
|
|
1269
|
+
vitest: "^4.1.11"
|
|
1270
|
+
},
|
|
1271
|
+
publishConfig: {
|
|
1272
|
+
access: "public"
|
|
1273
|
+
},
|
|
1274
|
+
license: "MIT",
|
|
1275
|
+
author: "Christian Crank",
|
|
1276
|
+
homepage: "https://github.com/stratum-hq/Stratum/tree/main/packages/mysql#readme",
|
|
1277
|
+
bugs: "https://github.com/stratum-hq/Stratum/issues",
|
|
1278
|
+
engines: {
|
|
1279
|
+
node: ">=20.0.0"
|
|
1280
|
+
},
|
|
1281
|
+
repository: {
|
|
1282
|
+
type: "git",
|
|
1283
|
+
url: "https://github.com/stratum-hq/Stratum.git",
|
|
1284
|
+
directory: "packages/mysql"
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
// ../nestjs/package.json
|
|
1289
|
+
var package_default6 = {
|
|
1290
|
+
name: "@stratum-hq/nestjs",
|
|
1291
|
+
version: "1.2.0",
|
|
1292
|
+
description: "Stratum NestJS integration \u2014 StratumGuard, @Tenant() decorator, and StratumModule",
|
|
1293
|
+
keywords: [
|
|
1294
|
+
"multi-tenancy",
|
|
1295
|
+
"multi-tenant",
|
|
1296
|
+
"saas",
|
|
1297
|
+
"tenant",
|
|
1298
|
+
"nestjs",
|
|
1299
|
+
"nest",
|
|
1300
|
+
"guard",
|
|
1301
|
+
"decorator",
|
|
1302
|
+
"middleware",
|
|
1303
|
+
"typescript",
|
|
1304
|
+
"stratum"
|
|
1305
|
+
],
|
|
1306
|
+
type: "module",
|
|
1307
|
+
main: "./dist/index.js",
|
|
1308
|
+
types: "./dist/index.d.ts",
|
|
1309
|
+
exports: {
|
|
1310
|
+
".": {
|
|
1311
|
+
types: "./dist/index.d.ts",
|
|
1312
|
+
default: "./dist/index.js"
|
|
1313
|
+
}
|
|
1314
|
+
},
|
|
1315
|
+
files: [
|
|
1316
|
+
"dist",
|
|
1317
|
+
"README.md",
|
|
1318
|
+
"LICENSE"
|
|
1319
|
+
],
|
|
1320
|
+
scripts: {
|
|
1321
|
+
build: "tsc",
|
|
1322
|
+
test: "vitest run",
|
|
1323
|
+
typecheck: "tsc --noEmit",
|
|
1324
|
+
lint: "eslint .",
|
|
1325
|
+
clean: "rm -rf dist"
|
|
1326
|
+
},
|
|
1327
|
+
publishConfig: {
|
|
1328
|
+
access: "public",
|
|
1329
|
+
registry: "https://registry.npmjs.org/"
|
|
1330
|
+
},
|
|
1331
|
+
peerDependencies: {
|
|
1332
|
+
"@nestjs/common": ">=10.0.0",
|
|
1333
|
+
"@nestjs/core": ">=10.0.0",
|
|
1334
|
+
"@stratum-hq/core": "^1.0.0",
|
|
1335
|
+
"@stratum-hq/sdk": "^1.2.0",
|
|
1336
|
+
"reflect-metadata": ">=0.1.13"
|
|
1337
|
+
},
|
|
1338
|
+
devDependencies: {
|
|
1339
|
+
"@nestjs/common": "^12.1.2",
|
|
1340
|
+
"@nestjs/core": "^12.1.2",
|
|
1341
|
+
"@nestjs/testing": "^12.1.2",
|
|
1342
|
+
"@stratum-hq/core": "*",
|
|
1343
|
+
"@stratum-hq/sdk": "*",
|
|
1344
|
+
"reflect-metadata": "^0.2.2",
|
|
1345
|
+
typescript: "^5.4.0",
|
|
1346
|
+
vitest: "^4.1.11"
|
|
1347
|
+
},
|
|
1348
|
+
license: "MIT",
|
|
1349
|
+
author: "Christian Crank",
|
|
1350
|
+
homepage: "https://github.com/stratum-hq/Stratum/tree/main/packages/nestjs#readme",
|
|
1351
|
+
bugs: "https://github.com/stratum-hq/Stratum/issues",
|
|
1352
|
+
engines: {
|
|
1353
|
+
node: ">=20.0.0"
|
|
1354
|
+
},
|
|
1355
|
+
repository: {
|
|
1356
|
+
type: "git",
|
|
1357
|
+
url: "https://github.com/stratum-hq/Stratum.git",
|
|
1358
|
+
directory: "packages/nestjs"
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1362
|
+
// src/stratum-versions.ts
|
|
1363
|
+
var STRATUM_RANGES = {
|
|
1364
|
+
"@stratum-hq/db-adapters": `^${package_default2.version}`,
|
|
1365
|
+
"@stratum-hq/hono": `^${package_default.version}`,
|
|
1366
|
+
"@stratum-hq/lib": `^${package_default3.version}`,
|
|
1367
|
+
"@stratum-hq/mongodb": `^${package_default4.version}`,
|
|
1368
|
+
"@stratum-hq/mysql": `^${package_default5.version}`,
|
|
1369
|
+
"@stratum-hq/nestjs": `^${package_default6.version}`
|
|
1370
|
+
};
|
|
1371
|
+
|
|
825
1372
|
// src/generators/package-json.ts
|
|
826
1373
|
function generatePresetPackageJson(projectName, preset) {
|
|
827
1374
|
const deps = {
|
|
828
|
-
"@stratum-hq/lib": "
|
|
1375
|
+
"@stratum-hq/lib": STRATUM_RANGES["@stratum-hq/lib"]
|
|
829
1376
|
};
|
|
830
1377
|
const devDeps = {
|
|
831
1378
|
typescript: "^5.3.0",
|
|
832
1379
|
"@types/node": "^20.11.0"
|
|
833
1380
|
};
|
|
834
|
-
|
|
1381
|
+
if (preset.framework === "nestjs") {
|
|
1382
|
+
devDeps["tsc-watch"] = "^7.2.0";
|
|
1383
|
+
} else if (preset.framework !== "nextjs") {
|
|
1384
|
+
devDeps["tsx"] = "^4.7.0";
|
|
1385
|
+
}
|
|
1386
|
+
addDatabaseDeps(deps, devDeps, preset);
|
|
835
1387
|
addOrmDeps(deps, devDeps, preset);
|
|
836
1388
|
addFrameworkDeps(deps, devDeps, preset);
|
|
837
1389
|
addStratumDeps(deps, preset);
|
|
@@ -853,9 +1405,12 @@ function generatePresetPackageJson(projectName, preset) {
|
|
|
853
1405
|
2
|
|
854
1406
|
);
|
|
855
1407
|
}
|
|
856
|
-
function addDatabaseDeps(deps, preset) {
|
|
1408
|
+
function addDatabaseDeps(deps, devDeps, preset) {
|
|
857
1409
|
switch (preset.database) {
|
|
858
1410
|
case "postgres":
|
|
1411
|
+
if (preset.orm === "pg" || preset.orm === "prisma" || preset.orm === "drizzle") {
|
|
1412
|
+
devDeps["@types/pg"] = "^8.11.0";
|
|
1413
|
+
}
|
|
859
1414
|
if (preset.orm !== "prisma" && preset.orm !== "drizzle" && preset.orm !== "sequelize") {
|
|
860
1415
|
deps["pg"] = "^8.11.0";
|
|
861
1416
|
}
|
|
@@ -945,29 +1500,29 @@ function addFrameworkDeps(deps, devDeps, preset) {
|
|
|
945
1500
|
}
|
|
946
1501
|
function addStratumDeps(deps, preset) {
|
|
947
1502
|
if (preset.database === "postgres" && preset.orm !== "mongoose") {
|
|
948
|
-
deps["@stratum-hq/db-adapters"] = "
|
|
1503
|
+
deps["@stratum-hq/db-adapters"] = STRATUM_RANGES["@stratum-hq/db-adapters"];
|
|
949
1504
|
}
|
|
950
1505
|
if (preset.database === "mongodb") {
|
|
951
|
-
deps["@stratum-hq/mongodb"] = "
|
|
1506
|
+
deps["@stratum-hq/mongodb"] = STRATUM_RANGES["@stratum-hq/mongodb"];
|
|
952
1507
|
}
|
|
953
1508
|
if (preset.database === "mysql") {
|
|
954
|
-
deps["@stratum-hq/mysql"] = "
|
|
1509
|
+
deps["@stratum-hq/mysql"] = STRATUM_RANGES["@stratum-hq/mysql"];
|
|
955
1510
|
}
|
|
956
1511
|
if (preset.framework === "hono") {
|
|
957
|
-
deps["@stratum-hq/hono"] = "
|
|
1512
|
+
deps["@stratum-hq/hono"] = STRATUM_RANGES["@stratum-hq/hono"];
|
|
958
1513
|
}
|
|
959
1514
|
if (preset.framework === "nestjs") {
|
|
960
|
-
deps["@stratum-hq/nestjs"] = "
|
|
1515
|
+
deps["@stratum-hq/nestjs"] = STRATUM_RANGES["@stratum-hq/nestjs"];
|
|
961
1516
|
}
|
|
962
1517
|
}
|
|
963
1518
|
function getScripts(preset) {
|
|
964
1519
|
if (preset.framework === "nextjs") {
|
|
965
1520
|
return { dev: "next dev", build: "next build", start: "next start" };
|
|
966
1521
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
}
|
|
970
|
-
return { dev
|
|
1522
|
+
const entry = preset.framework === "nestjs" ? "main" : "index";
|
|
1523
|
+
const emittedDir = rootSources(preset).length > 0 ? "dist/src" : "dist";
|
|
1524
|
+
const dev = preset.framework === "nestjs" ? `tsc-watch --onSuccess "node --env-file=.env ${emittedDir}/${entry}.js"` : `tsx watch --env-file=.env src/${entry}.ts`;
|
|
1525
|
+
return { dev, build: "tsc", start: `node ${emittedDir}/${entry}.js` };
|
|
971
1526
|
}
|
|
972
1527
|
function sortKeys(obj) {
|
|
973
1528
|
const sorted = {};
|
|
@@ -979,7 +1534,6 @@ function sortKeys(obj) {
|
|
|
979
1534
|
|
|
980
1535
|
// src/generators/readme.ts
|
|
981
1536
|
function generatePresetReadme(projectName, preset) {
|
|
982
|
-
const devCmd = getDevCommand(preset);
|
|
983
1537
|
const dbStartCmd = getDbStartInfo(preset);
|
|
984
1538
|
const dbSetupNote = getDbSetupNote(preset);
|
|
985
1539
|
return `# ${projectName}
|
|
@@ -1014,7 +1568,7 @@ ${dbSetupNote}
|
|
|
1014
1568
|
### 4. Run the app
|
|
1015
1569
|
|
|
1016
1570
|
\`\`\`bash
|
|
1017
|
-
|
|
1571
|
+
npm run dev
|
|
1018
1572
|
\`\`\`
|
|
1019
1573
|
|
|
1020
1574
|
## Multi-tenancy
|
|
@@ -1026,12 +1580,6 @@ ${getStrategyDescription(preset.strategy)}
|
|
|
1026
1580
|
See the [Stratum docs](https://github.com/stratum-hq/Stratum) for full reference.
|
|
1027
1581
|
`;
|
|
1028
1582
|
}
|
|
1029
|
-
function getDevCommand(preset) {
|
|
1030
|
-
if (preset.framework === "nextjs") return "npm run dev";
|
|
1031
|
-
if (preset.framework === "nestjs") return "node --env-file=.env src/main.ts";
|
|
1032
|
-
if (preset.framework === "none") return "node --env-file=.env src/index.ts";
|
|
1033
|
-
return "node --env-file=.env src/index.ts";
|
|
1034
|
-
}
|
|
1035
1583
|
function getDbStartInfo(preset) {
|
|
1036
1584
|
switch (preset.database) {
|
|
1037
1585
|
case "postgres":
|
|
@@ -1068,7 +1616,7 @@ function getStrategyDescription(strategy) {
|
|
|
1068
1616
|
switch (strategy) {
|
|
1069
1617
|
case "rls":
|
|
1070
1618
|
return `- **Row-Level Security** -- PostgreSQL RLS policies filter rows by tenant automatically
|
|
1071
|
-
- Each query sets \`app.
|
|
1619
|
+
- Each query sets \`app.current_tenant_id\` and RLS enforces isolation
|
|
1072
1620
|
- All tenants share one database and schema`;
|
|
1073
1621
|
case "schema":
|
|
1074
1622
|
return `- **Schema-per-tenant** -- each tenant gets a dedicated PostgreSQL schema
|
|
@@ -1172,27 +1720,7 @@ Creating ${projectName} with preset ${presetLabel}...
|
|
|
1172
1720
|
);
|
|
1173
1721
|
writeFile(
|
|
1174
1722
|
path.join(targetDir, "tsconfig.json"),
|
|
1175
|
-
|
|
1176
|
-
{
|
|
1177
|
-
compilerOptions: {
|
|
1178
|
-
target: "ESNext",
|
|
1179
|
-
module: "NodeNext",
|
|
1180
|
-
moduleResolution: "NodeNext",
|
|
1181
|
-
strict: true,
|
|
1182
|
-
outDir: "dist",
|
|
1183
|
-
rootDir: "src",
|
|
1184
|
-
declaration: true,
|
|
1185
|
-
skipLibCheck: true,
|
|
1186
|
-
esModuleInterop: true,
|
|
1187
|
-
...preset.framework === "nestjs" ? { experimentalDecorators: true, emitDecoratorMetadata: true } : {},
|
|
1188
|
-
...preset.framework === "nextjs" ? { jsx: "preserve", plugins: [{ name: "next" }] } : {}
|
|
1189
|
-
},
|
|
1190
|
-
include: ["src"],
|
|
1191
|
-
exclude: ["node_modules", "dist"]
|
|
1192
|
-
},
|
|
1193
|
-
null,
|
|
1194
|
-
2
|
|
1195
|
-
)
|
|
1723
|
+
generateTsconfig(preset.framework, rootSources(preset))
|
|
1196
1724
|
);
|
|
1197
1725
|
if (!skipInstall) {
|
|
1198
1726
|
console.log("\nInstalling dependencies...\n");
|
|
@@ -1295,7 +1823,7 @@ function generatePackageJson(projectName, template) {
|
|
|
1295
1823
|
}
|
|
1296
1824
|
};
|
|
1297
1825
|
const deps = {
|
|
1298
|
-
"@stratum-hq/lib": "
|
|
1826
|
+
"@stratum-hq/lib": STRATUM_RANGES["@stratum-hq/lib"],
|
|
1299
1827
|
pg: "^8.11.0",
|
|
1300
1828
|
...frameworkDeps[template]
|
|
1301
1829
|
};
|
|
@@ -1305,11 +1833,15 @@ function generatePackageJson(projectName, template) {
|
|
|
1305
1833
|
version: "0.1.0",
|
|
1306
1834
|
private: true,
|
|
1307
1835
|
type: "module",
|
|
1308
|
-
scripts: template === "nextjs" ? { dev: "next dev", build: "next build", start: "next start" } :
|
|
1836
|
+
scripts: template === "nextjs" ? { dev: "next dev", build: "next build", start: "next start" } : (
|
|
1837
|
+
// Node 20 cannot run a .ts file, so dev runs the source through tsx.
|
|
1838
|
+
{ dev: "tsx watch --env-file=.env src/index.ts", build: "tsc", start: "node dist/index.js" }
|
|
1839
|
+
),
|
|
1309
1840
|
dependencies: deps,
|
|
1310
1841
|
devDependencies: {
|
|
1311
1842
|
typescript: "^5.3.0",
|
|
1312
|
-
"@types/node": "^20.11.0"
|
|
1843
|
+
"@types/node": "^20.11.0",
|
|
1844
|
+
...template === "nextjs" ? {} : { tsx: "^4.7.0", "@types/pg": "^8.11.0" }
|
|
1313
1845
|
},
|
|
1314
1846
|
engines: {
|
|
1315
1847
|
node: ">=20.0.0"
|
|
@@ -1493,7 +2025,6 @@ export const config = {
|
|
|
1493
2025
|
`;
|
|
1494
2026
|
}
|
|
1495
2027
|
function generateReadme(projectName, template) {
|
|
1496
|
-
const devCmd = template === "nextjs" ? "npm run dev" : "node --env-file=.env src/index.ts";
|
|
1497
2028
|
return `# ${projectName}
|
|
1498
2029
|
|
|
1499
2030
|
A multi-tenant application built with [Stratum](https://github.com/stratum-hq/Stratum).
|
|
@@ -1522,7 +2053,7 @@ npm install
|
|
|
1522
2053
|
### 4. Run the app
|
|
1523
2054
|
|
|
1524
2055
|
\`\`\`bash
|
|
1525
|
-
|
|
2056
|
+
npm run dev
|
|
1526
2057
|
\`\`\`
|
|
1527
2058
|
|
|
1528
2059
|
## Project structure
|
|
@@ -1533,7 +2064,7 @@ ${projectName}/
|
|
|
1533
2064
|
\u251C\u2500\u2500 docker-compose.yml
|
|
1534
2065
|
\u251C\u2500\u2500 init.sql # DB extensions (uuid-ossp, ltree)
|
|
1535
2066
|
\u251C\u2500\u2500 .env.example
|
|
1536
|
-
\u2514\u2500\u2500 package.json
|
|
2067
|
+
${template === "nextjs" ? "" : "\u251C\u2500\u2500 tsconfig.json\n"}\u2514\u2500\u2500 package.json
|
|
1537
2068
|
\`\`\`
|
|
1538
2069
|
|
|
1539
2070
|
## Multi-tenancy
|
|
@@ -1560,8 +2091,10 @@ Creating ${projectName} with ${template} template...
|
|
|
1560
2091
|
writeFile2(path2.join(targetDir, ".env.example"), generateEnv(projectName));
|
|
1561
2092
|
if (template === "express") {
|
|
1562
2093
|
writeFile2(path2.join(targetDir, "src", "index.ts"), generateExpressServer(projectName));
|
|
2094
|
+
writeFile2(path2.join(targetDir, "tsconfig.json"), generateTsconfig(template));
|
|
1563
2095
|
} else if (template === "fastify") {
|
|
1564
2096
|
writeFile2(path2.join(targetDir, "src", "index.ts"), generateFastifyServer(projectName));
|
|
2097
|
+
writeFile2(path2.join(targetDir, "tsconfig.json"), generateTsconfig(template));
|
|
1565
2098
|
} else if (template === "nextjs") {
|
|
1566
2099
|
writeFile2(path2.join(targetDir, "src", "app", "page.tsx"), generateNextjsPage(projectName));
|
|
1567
2100
|
writeFile2(path2.join(targetDir, "middleware.ts"), generateNextjsMiddleware2());
|
|
@@ -1639,26 +2172,9 @@ Success! Created ${projectName} at ${targetDir}
|
|
|
1639
2172
|
if (skipInstall) {
|
|
1640
2173
|
console.log(" npm install");
|
|
1641
2174
|
}
|
|
1642
|
-
|
|
1643
|
-
const parsed = parsePresetString(preset);
|
|
1644
|
-
if (parsed.framework === "nextjs") {
|
|
1645
|
-
console.log(" npm run dev");
|
|
1646
|
-
} else if (parsed.framework === "nestjs") {
|
|
1647
|
-
console.log(" node --env-file=.env src/main.ts");
|
|
1648
|
-
} else {
|
|
1649
|
-
console.log(" node --env-file=.env src/index.ts");
|
|
1650
|
-
}
|
|
1651
|
-
} else if (template === "nextjs") {
|
|
1652
|
-
console.log(" npm run dev");
|
|
1653
|
-
} else {
|
|
1654
|
-
console.log(" node --env-file=.env src/index.ts");
|
|
1655
|
-
}
|
|
2175
|
+
console.log(" npm run dev");
|
|
1656
2176
|
console.log("");
|
|
1657
2177
|
}
|
|
1658
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
1659
|
-
if (process.argv[1] === __filename) {
|
|
1660
|
-
main(process.argv.slice(2));
|
|
1661
|
-
}
|
|
1662
2178
|
export {
|
|
1663
2179
|
createProject,
|
|
1664
2180
|
main,
|