@augmenting-integrations/create-tenant 8.6.0 → 8.7.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 +15 -8
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,12 +9,15 @@ pnpm dlx @augmenting-integrations/create-tenant acme --apex=acme.com
|
|
|
9
9
|
## What gets generated
|
|
10
10
|
|
|
11
11
|
The Next.js 16 apex for a new tenant — the single OAuth broker and the
|
|
12
|
-
|
|
12
|
+
runtime serving point for the tenant app roster:
|
|
13
13
|
|
|
14
14
|
- `/api/auth/[...nextauth]` — the ONLY Cognito callback in the tenant
|
|
15
15
|
ecosystem; sets the parent-domain session cookie
|
|
16
|
-
- `/api/apps` —
|
|
17
|
-
|
|
16
|
+
- `/api/apps` — static-roster handler reading `config/apps.json`,
|
|
17
|
+
filtered by user identity groups. Spokes proxy here for cross-app nav.
|
|
18
|
+
- `app.manifest.json` declaring this apex
|
|
19
|
+
- `config/apps.json` seeded with just the apex entry (add spokes as you
|
|
20
|
+
scaffold them)
|
|
18
21
|
- `/login` — Cognito sign-in entry point (spokes redirect here)
|
|
19
22
|
- `loadTenantConfig({ role: "apex" })` + `<TenantBootScript>` +
|
|
20
23
|
`<TenantProvider>` so spokes see the same tenant struct
|
|
@@ -27,11 +30,12 @@ The generated app is portable; the AWS infra is not:
|
|
|
27
30
|
2. Cognito user pool + one App Client with ONE callback URL:
|
|
28
31
|
`https://<your-apex>/api/auth/callback/cognito`
|
|
29
32
|
3. Hosted zone + cert for `<your-apex>` and `*.<your-apex>`
|
|
30
|
-
4.
|
|
31
|
-
5.
|
|
32
|
-
6.
|
|
33
|
-
7. SAM `template.yaml` (copy from an existing apex like
|
|
33
|
+
4. Secrets Manager rows for `AUTH_SECRET`, `AUTH_COGNITO_SECRET`
|
|
34
|
+
5. GitHub OIDC role in each AWS account
|
|
35
|
+
6. SAM `template.yaml` (copy from an existing apex like
|
|
34
36
|
`augint-example-web` and adapt)
|
|
37
|
+
7. A central infra repo (`<tenant>-infra`) with `config/apps.yaml`
|
|
38
|
+
declaring this tenant's app roster
|
|
35
39
|
|
|
36
40
|
## Adding spokes after the apex exists
|
|
37
41
|
|
|
@@ -39,4 +43,7 @@ The generated app is portable; the AWS infra is not:
|
|
|
39
43
|
pnpm dlx @augmenting-integrations/create-spoke my-product-spoke
|
|
40
44
|
```
|
|
41
45
|
|
|
42
|
-
Then
|
|
46
|
+
Then add the new spoke's entry to `<tenant>-infra/config/apps.yaml` AND
|
|
47
|
+
to this apex repo's `config/apps.json`. `pnpm exec augint validate-app-roster`
|
|
48
|
+
enforces the two files agree. The spoke's `/api/apps` proxies here, so
|
|
49
|
+
no per-spoke roster maintenance is needed.
|
package/dist/cli.js
CHANGED
|
@@ -70,7 +70,7 @@ function main() {
|
|
|
70
70
|
console.log(" pnpm install");
|
|
71
71
|
console.log(" pnpm dev");
|
|
72
72
|
console.log("");
|
|
73
|
-
console.log("Then provision tenant infra (Cognito + DNS +
|
|
73
|
+
console.log("Then provision tenant infra (Cognito + DNS + config/apps.yaml roster):");
|
|
74
74
|
console.log(" see README.md");
|
|
75
75
|
}
|
|
76
76
|
main();
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\n\n// =============================================================================\n// create-tenant -- scaffold a new tenant apex app.\n//\n// Usage:\n// pnpm dlx @augmenting-integrations/create-tenant <name> [--apex=foo.com]\n//\n// Writes a minimal but deployable Next 16 apex to ./<name>/ that:\n// - Mounts /api/auth/[...nextauth] -- the ONLY Cognito OAuth callback in\n// the tenant ecosystem\n// - Exposes /api/apps (static-roster handler, read by every spoke's\n// AppShell for cross-app nav)\n// - Wires loadTenantConfig({ role: \"apex\" }) + TenantBootScript +\n// TenantProvider so spokes can read the same tenant struct\n// - Includes a /studio admin landing page stub\n//\n// Out of scope (manual follow-up, documented in the generated README):\n// - Cognito User Pool + App Client provisioning\n// - DNS hosted zone + cert\n// - Tenant app roster file (config/apps.yaml in <tenant>-infra and\n// config/apps.json mirror in the apex repo)\n// - GitHub OIDC role for deploys\n// - First spoke (use create-spoke for each product subdomain)\n// =============================================================================\n\ntype Flags = {\n name: string;\n apex: string;\n};\n\nfunction parseArgs(argv: string[]): Flags {\n const args = argv.slice(2);\n let name: string | undefined;\n let apex: string | undefined;\n for (const a of args) {\n if (a.startsWith(\"--apex=\")) apex = a.slice(\"--apex=\".length);\n else if (!a.startsWith(\"--\")) name = a;\n }\n if (!name) {\n console.error(\"Usage: create-tenant <name> [--apex=tenant.example.com]\");\n process.exit(1);\n }\n return { name, apex: apex ?? `${name}.example.com` };\n}\n\nfunction templatesDir(): string {\n const here = fileURLToPath(import.meta.url);\n return path.resolve(path.dirname(here), \"..\", \"templates\");\n}\n\nfunction ensureEmptyDir(target: string): void {\n if (fs.existsSync(target)) {\n const entries = fs.readdirSync(target);\n if (entries.length > 0) {\n console.error(`Refusing to write into non-empty directory: ${target}`);\n process.exit(1);\n }\n } else {\n fs.mkdirSync(target, { recursive: true });\n }\n}\n\nfunction replaceVars(content: string, flags: Flags): string {\n return content\n .replace(/__TENANT_NAME__/g, flags.name)\n .replace(/__TENANT_APEX__/g, flags.apex)\n .replace(/__TENANT_PARENT__/g, `.${flags.apex}`);\n}\n\nfunction copyTree(src: string, dst: string, flags: Flags): void {\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n const srcPath = path.join(src, entry.name);\n const baseName = entry.name.endsWith(\".tmpl\")\n ? entry.name.slice(0, -\".tmpl\".length)\n : entry.name;\n const dstPath = path.join(dst, replaceVars(baseName, flags));\n if (entry.isDirectory()) {\n fs.mkdirSync(dstPath, { recursive: true });\n copyTree(srcPath, dstPath, flags);\n } else {\n const raw = fs.readFileSync(srcPath, \"utf8\");\n fs.writeFileSync(dstPath, replaceVars(raw, flags), \"utf8\");\n }\n }\n}\n\nfunction main(): void {\n const flags = parseArgs(process.argv);\n const target = path.resolve(process.cwd(), flags.name);\n ensureEmptyDir(target);\n const templates = templatesDir();\n if (!fs.existsSync(templates)) {\n console.error(`Templates directory missing at ${templates}. Re-install the CLI.`);\n process.exit(1);\n }\n copyTree(templates, target, flags);\n console.log(`Created apex \"${flags.name}\" at ${target}`);\n console.log(`Apex domain: ${flags.apex}`);\n console.log(\"\");\n console.log(\"Next steps:\");\n console.log(` cd ${flags.name}`);\n console.log(\" cp .env.example .env # fill in Cognito ARNs + AWS resources\");\n console.log(\" pnpm install\");\n console.log(\" pnpm dev\");\n console.log(\"\");\n console.log(\"Then provision tenant infra (Cognito + DNS +
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\n\n// =============================================================================\n// create-tenant -- scaffold a new tenant apex app.\n//\n// Usage:\n// pnpm dlx @augmenting-integrations/create-tenant <name> [--apex=foo.com]\n//\n// Writes a minimal but deployable Next 16 apex to ./<name>/ that:\n// - Mounts /api/auth/[...nextauth] -- the ONLY Cognito OAuth callback in\n// the tenant ecosystem\n// - Exposes /api/apps (static-roster handler, read by every spoke's\n// AppShell for cross-app nav)\n// - Wires loadTenantConfig({ role: \"apex\" }) + TenantBootScript +\n// TenantProvider so spokes can read the same tenant struct\n// - Includes a /studio admin landing page stub\n//\n// Out of scope (manual follow-up, documented in the generated README):\n// - Cognito User Pool + App Client provisioning\n// - DNS hosted zone + cert\n// - Tenant app roster file (config/apps.yaml in <tenant>-infra and\n// config/apps.json mirror in the apex repo)\n// - GitHub OIDC role for deploys\n// - First spoke (use create-spoke for each product subdomain)\n// =============================================================================\n\ntype Flags = {\n name: string;\n apex: string;\n};\n\nfunction parseArgs(argv: string[]): Flags {\n const args = argv.slice(2);\n let name: string | undefined;\n let apex: string | undefined;\n for (const a of args) {\n if (a.startsWith(\"--apex=\")) apex = a.slice(\"--apex=\".length);\n else if (!a.startsWith(\"--\")) name = a;\n }\n if (!name) {\n console.error(\"Usage: create-tenant <name> [--apex=tenant.example.com]\");\n process.exit(1);\n }\n return { name, apex: apex ?? `${name}.example.com` };\n}\n\nfunction templatesDir(): string {\n const here = fileURLToPath(import.meta.url);\n return path.resolve(path.dirname(here), \"..\", \"templates\");\n}\n\nfunction ensureEmptyDir(target: string): void {\n if (fs.existsSync(target)) {\n const entries = fs.readdirSync(target);\n if (entries.length > 0) {\n console.error(`Refusing to write into non-empty directory: ${target}`);\n process.exit(1);\n }\n } else {\n fs.mkdirSync(target, { recursive: true });\n }\n}\n\nfunction replaceVars(content: string, flags: Flags): string {\n return content\n .replace(/__TENANT_NAME__/g, flags.name)\n .replace(/__TENANT_APEX__/g, flags.apex)\n .replace(/__TENANT_PARENT__/g, `.${flags.apex}`);\n}\n\nfunction copyTree(src: string, dst: string, flags: Flags): void {\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n const srcPath = path.join(src, entry.name);\n const baseName = entry.name.endsWith(\".tmpl\")\n ? entry.name.slice(0, -\".tmpl\".length)\n : entry.name;\n const dstPath = path.join(dst, replaceVars(baseName, flags));\n if (entry.isDirectory()) {\n fs.mkdirSync(dstPath, { recursive: true });\n copyTree(srcPath, dstPath, flags);\n } else {\n const raw = fs.readFileSync(srcPath, \"utf8\");\n fs.writeFileSync(dstPath, replaceVars(raw, flags), \"utf8\");\n }\n }\n}\n\nfunction main(): void {\n const flags = parseArgs(process.argv);\n const target = path.resolve(process.cwd(), flags.name);\n ensureEmptyDir(target);\n const templates = templatesDir();\n if (!fs.existsSync(templates)) {\n console.error(`Templates directory missing at ${templates}. Re-install the CLI.`);\n process.exit(1);\n }\n copyTree(templates, target, flags);\n console.log(`Created apex \"${flags.name}\" at ${target}`);\n console.log(`Apex domain: ${flags.apex}`);\n console.log(\"\");\n console.log(\"Next steps:\");\n console.log(` cd ${flags.name}`);\n console.log(\" cp .env.example .env # fill in Cognito ARNs + AWS resources\");\n console.log(\" pnpm install\");\n console.log(\" pnpm dev\");\n console.log(\"\");\n console.log(\"Then provision tenant infra (Cognito + DNS + config/apps.yaml roster):\");\n console.log(\" see README.md\");\n}\n\nmain();\n"],"mappings":";;;AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,qBAAqB;AAC9B,OAAO,aAAa;AA+BpB,SAAS,UAAU,MAAuB;AACxC,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,MAAI;AACJ,MAAI;AACJ,aAAW,KAAK,MAAM;AACpB,QAAI,EAAE,WAAW,SAAS,EAAG,QAAO,EAAE,MAAM,UAAU,MAAM;AAAA,aACnD,CAAC,EAAE,WAAW,IAAI,EAAG,QAAO;AAAA,EACvC;AACA,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,yDAAyD;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO,EAAE,MAAM,MAAM,QAAQ,GAAG,IAAI,eAAe;AACrD;AAEA,SAAS,eAAuB;AAC9B,QAAM,OAAO,cAAc,YAAY,GAAG;AAC1C,SAAY,aAAa,aAAQ,IAAI,GAAG,MAAM,WAAW;AAC3D;AAEA,SAAS,eAAe,QAAsB;AAC5C,MAAO,cAAW,MAAM,GAAG;AACzB,UAAM,UAAa,eAAY,MAAM;AACrC,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,MAAM,+CAA+C,MAAM,EAAE;AACrE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,OAAO;AACL,IAAG,aAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AACF;AAEA,SAAS,YAAY,SAAiB,OAAsB;AAC1D,SAAO,QACJ,QAAQ,oBAAoB,MAAM,IAAI,EACtC,QAAQ,oBAAoB,MAAM,IAAI,EACtC,QAAQ,sBAAsB,IAAI,MAAM,IAAI,EAAE;AACnD;AAEA,SAAS,SAAS,KAAa,KAAa,OAAoB;AAC9D,aAAW,SAAY,eAAY,KAAK,EAAE,eAAe,KAAK,CAAC,GAAG;AAChE,UAAM,UAAe,UAAK,KAAK,MAAM,IAAI;AACzC,UAAM,WAAW,MAAM,KAAK,SAAS,OAAO,IACxC,MAAM,KAAK,MAAM,GAAG,CAAC,QAAQ,MAAM,IACnC,MAAM;AACV,UAAM,UAAe,UAAK,KAAK,YAAY,UAAU,KAAK,CAAC;AAC3D,QAAI,MAAM,YAAY,GAAG;AACvB,MAAG,aAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AACzC,eAAS,SAAS,SAAS,KAAK;AAAA,IAClC,OAAO;AACL,YAAM,MAAS,gBAAa,SAAS,MAAM;AAC3C,MAAG,iBAAc,SAAS,YAAY,KAAK,KAAK,GAAG,MAAM;AAAA,IAC3D;AAAA,EACF;AACF;AAEA,SAAS,OAAa;AACpB,QAAM,QAAQ,UAAU,QAAQ,IAAI;AACpC,QAAM,SAAc,aAAQ,QAAQ,IAAI,GAAG,MAAM,IAAI;AACrD,iBAAe,MAAM;AACrB,QAAM,YAAY,aAAa;AAC/B,MAAI,CAAI,cAAW,SAAS,GAAG;AAC7B,YAAQ,MAAM,kCAAkC,SAAS,uBAAuB;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,WAAS,WAAW,QAAQ,KAAK;AACjC,UAAQ,IAAI,iBAAiB,MAAM,IAAI,QAAQ,MAAM,EAAE;AACvD,UAAQ,IAAI,gBAAgB,MAAM,IAAI,EAAE;AACxC,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,aAAa;AACzB,UAAQ,IAAI,QAAQ,MAAM,IAAI,EAAE;AAChC,UAAQ,IAAI,gEAAgE;AAC5E,UAAQ,IAAI,gBAAgB;AAC5B,UAAQ,IAAI,YAAY;AACxB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,wEAAwE;AACpF,UAAQ,IAAI,iBAAiB;AAC/B;AAEA,KAAK;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@augmenting-integrations/create-tenant",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.7.0",
|
|
4
4
|
"description": "Scaffold a new tenant apex app for an augint deployment. Generates a Next 16 + Auth.js v5 + Cognito apex with TenantConfig wired up, library-owned /api/apps registry handler, and /studio admin. Single command: pnpm dlx @augmenting-integrations/create-tenant my-tenant.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|