@augmenting-integrations/deploy-tools 8.4.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 +47 -0
- package/dist/canonical/schema.d.ts +25 -0
- package/dist/canonical/schema.d.ts.map +1 -0
- package/dist/chunk-A7IIB34G.js +230 -0
- package/dist/chunk-A7IIB34G.js.map +1 -0
- package/dist/chunk-Q5W2NBVE.js +231 -0
- package/dist/chunk-Q5W2NBVE.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +41 -0
- package/dist/cli.js.map +1 -0
- package/dist/package-next-lambda.d.ts +2 -0
- package/dist/package-next-lambda.d.ts.map +1 -0
- package/dist/package-next-lambda.js +8 -0
- package/dist/package-next-lambda.js.map +1 -0
- package/dist/prisma-parser.d.ts +11 -0
- package/dist/prisma-parser.d.ts.map +1 -0
- package/dist/validate-spoke.d.ts +2 -0
- package/dist/validate-spoke.d.ts.map +1 -0
- package/dist/validate-spoke.js +8 -0
- package/dist/validate-spoke.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @augmenting-integrations/deploy-tools
|
|
2
|
+
|
|
3
|
+
Augint platform deploy tooling. One npm package shared across every spoke
|
|
4
|
+
and apex repo so deploy logic isn't copy/pasted into each app's CI
|
|
5
|
+
workflow.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Validate the spoke's Prisma schema against the canonical contract
|
|
11
|
+
# implied by app.manifest.json (User / Invitation / PaymentMethod /
|
|
12
|
+
# CreditTransaction / ActivityLog, gated by features.*).
|
|
13
|
+
pnpm exec augint validate-spoke
|
|
14
|
+
pnpm exec augint-validate-spoke # alternate binary, same behavior
|
|
15
|
+
|
|
16
|
+
# Package the Next standalone build for AWS Lambda. Lifts the symlink
|
|
17
|
+
# flatten loop, .pnpm purge, Prisma hoist, and engine trim that every
|
|
18
|
+
# spoke deploy needs.
|
|
19
|
+
pnpm exec augint package-next-lambda
|
|
20
|
+
pnpm exec augint-package-next-lambda # alternate binary, same behavior
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Both subcommands fail loudly with a field-by-field error list if the
|
|
24
|
+
manifest or schema is inconsistent.
|
|
25
|
+
|
|
26
|
+
## Wiring into CI
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
- run: pnpm exec augint validate-spoke
|
|
30
|
+
- run: pnpm build
|
|
31
|
+
- run: pnpm exec augint package-next-lambda
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Canonical schema contract
|
|
35
|
+
|
|
36
|
+
The validator enforces the schema fragments the library handler factories
|
|
37
|
+
read. Specifically:
|
|
38
|
+
|
|
39
|
+
- Always required: `User`
|
|
40
|
+
- When `features.invitations`: `Invitation`
|
|
41
|
+
- When `features.billing`: `PaymentMethod`, `CreditTransaction`
|
|
42
|
+
- When `features.impersonation` or `features.invitations`: `ActivityLog`
|
|
43
|
+
|
|
44
|
+
Within each model, the required field names match what the library
|
|
45
|
+
handlers query. Missing fields fail validation by name (the validator
|
|
46
|
+
does not deep-check Prisma types -- the type system catches drift via
|
|
47
|
+
the platform adapter in `src/platform/repositories.ts`).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AppManifest } from "@augmenting-integrations/platform/manifest";
|
|
2
|
+
export type RequiredField = {
|
|
3
|
+
name: string;
|
|
4
|
+
/**
|
|
5
|
+
* Reason this field is required (shown in the error message). Helps
|
|
6
|
+
* developers understand WHY the validator cares, not just WHAT it
|
|
7
|
+
* found missing.
|
|
8
|
+
*/
|
|
9
|
+
reason: string;
|
|
10
|
+
};
|
|
11
|
+
export type RequiredModel = {
|
|
12
|
+
name: string;
|
|
13
|
+
fields: RequiredField[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Returns the list of required models for a given manifest, in priority
|
|
17
|
+
* order. Each model lists the fields its library consumers read.
|
|
18
|
+
*
|
|
19
|
+
* `User` is always required.
|
|
20
|
+
* `Invitation` is required when features.invitations.
|
|
21
|
+
* `PaymentMethod` + `CreditTransaction` are required when features.billing.
|
|
22
|
+
* `ActivityLog` is required when features.impersonation or features.invitations.
|
|
23
|
+
*/
|
|
24
|
+
export declare function requiredModelsForManifest(manifest: AppManifest): RequiredModel[];
|
|
25
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/canonical/schema.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAC;AAE9E,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,WAAW,GAAG,aAAa,EAAE,CAwGhF"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/validate-spoke.ts
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
import { resolve, join } from "path";
|
|
6
|
+
import process from "process";
|
|
7
|
+
import { loadManifest } from "@augmenting-integrations/platform/manifest";
|
|
8
|
+
|
|
9
|
+
// src/canonical/schema.ts
|
|
10
|
+
function requiredModelsForManifest(manifest) {
|
|
11
|
+
const out = [];
|
|
12
|
+
out.push({
|
|
13
|
+
name: "User",
|
|
14
|
+
fields: [
|
|
15
|
+
{ name: "id", reason: "JIT lookup + impersonation token claims" },
|
|
16
|
+
{ name: "email", reason: "JIT email lookup + auto-promote-to-admin" },
|
|
17
|
+
{ name: "name", reason: "/api/auth/me, UserMenu display" },
|
|
18
|
+
{ name: "role", reason: "JIT role resolution + admin gating" },
|
|
19
|
+
{ name: "is_active", reason: "/api/auth/me response shape" },
|
|
20
|
+
{ name: "credit_balance", reason: "JIT initial balance + /api/auth/me" },
|
|
21
|
+
{ name: "password", reason: "schema-inherited not-null filler at JIT create" },
|
|
22
|
+
{ name: "parent_id", reason: "invitation auto-accept sets parent_id from invite" },
|
|
23
|
+
{ name: "created_at", reason: "audit / ordering" },
|
|
24
|
+
{ name: "updated_at", reason: "audit / ordering" }
|
|
25
|
+
]
|
|
26
|
+
});
|
|
27
|
+
if (manifest.features.billing) {
|
|
28
|
+
out[0].fields.push(
|
|
29
|
+
{ name: "stripe_customer_id", reason: "createBillingHandlers user shape" },
|
|
30
|
+
{
|
|
31
|
+
name: "stripe_default_payment_method",
|
|
32
|
+
reason: "createBillingHandlers user shape"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "stripe_default_payment_method_display",
|
|
36
|
+
reason: "createBillingHandlers user shape"
|
|
37
|
+
},
|
|
38
|
+
{ name: "auto_recharge_enabled", reason: "/api/billing/auto-recharge" },
|
|
39
|
+
{ name: "auto_recharge_threshold", reason: "/api/billing/auto-recharge" },
|
|
40
|
+
{ name: "auto_recharge_amount", reason: "/api/billing/auto-recharge" }
|
|
41
|
+
);
|
|
42
|
+
out.push({
|
|
43
|
+
name: "PaymentMethod",
|
|
44
|
+
fields: [
|
|
45
|
+
{ name: "id", reason: "createBillingHandlers row shape" },
|
|
46
|
+
{ name: "user_id", reason: "createBillingHandlers row shape" },
|
|
47
|
+
{
|
|
48
|
+
name: "stripe_payment_method_id",
|
|
49
|
+
reason: "createBillingHandlers row shape (unique key)"
|
|
50
|
+
},
|
|
51
|
+
{ name: "brand", reason: "createBillingHandlers row shape" },
|
|
52
|
+
{ name: "last4", reason: "createBillingHandlers row shape" },
|
|
53
|
+
{ name: "exp_month", reason: "createBillingHandlers row shape" },
|
|
54
|
+
{ name: "exp_year", reason: "createBillingHandlers row shape" },
|
|
55
|
+
{ name: "is_default", reason: "createBillingHandlers row shape" },
|
|
56
|
+
{ name: "created_at", reason: "createBillingHandlers row shape" }
|
|
57
|
+
]
|
|
58
|
+
});
|
|
59
|
+
out.push({
|
|
60
|
+
name: "CreditTransaction",
|
|
61
|
+
fields: [
|
|
62
|
+
{ name: "id", reason: "createBillingHandlers row shape" },
|
|
63
|
+
{ name: "user_id", reason: "createBillingHandlers row shape" },
|
|
64
|
+
{ name: "type", reason: "createBillingHandlers row shape" },
|
|
65
|
+
{ name: "amount", reason: "createBillingHandlers row shape" },
|
|
66
|
+
{ name: "description", reason: "createBillingHandlers row shape" },
|
|
67
|
+
{
|
|
68
|
+
name: "stripe_payment_intent_id",
|
|
69
|
+
reason: "createBillingHandlers webhook idempotency"
|
|
70
|
+
},
|
|
71
|
+
{ name: "payment_method_display", reason: "createBillingHandlers row shape" },
|
|
72
|
+
{ name: "created_at", reason: "createBillingHandlers row shape" }
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (manifest.features.invitations) {
|
|
77
|
+
out.push({
|
|
78
|
+
name: "Invitation",
|
|
79
|
+
fields: [
|
|
80
|
+
{ name: "id", reason: "createInvitationHandlers + JIT invitation accept" },
|
|
81
|
+
{ name: "token", reason: "createInvitationHandlers findUnique" },
|
|
82
|
+
{ name: "email", reason: "JIT invitation lookup" },
|
|
83
|
+
{ name: "intended_role", reason: "JIT role inheritance" },
|
|
84
|
+
{ name: "parent_id", reason: "JIT parent_id inheritance" },
|
|
85
|
+
{ name: "expires_at", reason: "JIT expiry filter" },
|
|
86
|
+
{ name: "accepted_at", reason: "JIT acceptance toggle" },
|
|
87
|
+
{
|
|
88
|
+
name: "accepted_by_user_id",
|
|
89
|
+
reason: "JIT writes acceptance with new user id"
|
|
90
|
+
},
|
|
91
|
+
{ name: "created_at", reason: "ordering" }
|
|
92
|
+
]
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (manifest.features.impersonation || manifest.features.invitations) {
|
|
96
|
+
out.push({
|
|
97
|
+
name: "ActivityLog",
|
|
98
|
+
fields: [
|
|
99
|
+
{ name: "id", reason: "audit row" },
|
|
100
|
+
{ name: "user_id", reason: "createImpersonateHandlers audit writes" },
|
|
101
|
+
{ name: "action", reason: "createImpersonateHandlers audit writes" },
|
|
102
|
+
{ name: "metadata", reason: "createImpersonateHandlers audit writes" },
|
|
103
|
+
{ name: "created_at", reason: "audit row" }
|
|
104
|
+
]
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/prisma-parser.ts
|
|
111
|
+
function parsePrismaSchema(source) {
|
|
112
|
+
const out = [];
|
|
113
|
+
const stripped = source.replace(/\/\/[^\n]*/g, "");
|
|
114
|
+
const modelRegex = /model\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{([\s\S]*?)\}/g;
|
|
115
|
+
for (const match of stripped.matchAll(modelRegex)) {
|
|
116
|
+
const name = match[1];
|
|
117
|
+
const body = match[2];
|
|
118
|
+
const fields = [];
|
|
119
|
+
for (const rawLine of body.split("\n")) {
|
|
120
|
+
const line = rawLine.trim();
|
|
121
|
+
if (line === "") continue;
|
|
122
|
+
if (line.startsWith("@@")) continue;
|
|
123
|
+
if (line.startsWith("@")) continue;
|
|
124
|
+
const fieldMatch = /^([A-Za-z_][A-Za-z0-9_]*)\s+/.exec(line);
|
|
125
|
+
if (fieldMatch) fields.push(fieldMatch[1]);
|
|
126
|
+
}
|
|
127
|
+
out.push({ name, fields });
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/validate-spoke.ts
|
|
133
|
+
async function runValidateSpoke(argv) {
|
|
134
|
+
let cwd = process.cwd();
|
|
135
|
+
for (let i = 0; i < argv.length; i++) {
|
|
136
|
+
const a = argv[i];
|
|
137
|
+
if (a === "--cwd" || a === "--root") {
|
|
138
|
+
const next = argv[i + 1];
|
|
139
|
+
if (next) {
|
|
140
|
+
cwd = resolve(next);
|
|
141
|
+
i++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
let manifest;
|
|
146
|
+
try {
|
|
147
|
+
manifest = loadManifest({ cwd });
|
|
148
|
+
} catch (err) {
|
|
149
|
+
process.stderr.write(`validate-spoke: ${err.message}
|
|
150
|
+
`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
if (manifest.dataPlane.type === "none") {
|
|
154
|
+
process.stdout.write(
|
|
155
|
+
`validate-spoke: ${manifest.appSlug} has dataPlane.type=none; nothing to validate.
|
|
156
|
+
`
|
|
157
|
+
);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const schemaPath = join(cwd, "prisma", "schema.prisma");
|
|
161
|
+
if (!existsSync(schemaPath)) {
|
|
162
|
+
process.stderr.write(
|
|
163
|
+
`validate-spoke: prisma/schema.prisma not found at ${schemaPath}
|
|
164
|
+
Manifest declares a data plane but the schema file is missing.
|
|
165
|
+
`
|
|
166
|
+
);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
const schemaSrc = readFileSync(schemaPath, "utf8");
|
|
170
|
+
const parsed = parsePrismaSchema(schemaSrc);
|
|
171
|
+
const required = requiredModelsForManifest(manifest);
|
|
172
|
+
const failures = [];
|
|
173
|
+
for (const req of required) {
|
|
174
|
+
const found = parsed.find((m) => m.name === req.name);
|
|
175
|
+
if (!found) {
|
|
176
|
+
failures.push({ kind: "missing_model", model: req.name });
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const has = new Set(found.fields);
|
|
180
|
+
for (const field of req.fields) {
|
|
181
|
+
if (!has.has(field.name)) {
|
|
182
|
+
failures.push({
|
|
183
|
+
kind: "missing_field",
|
|
184
|
+
model: req.name,
|
|
185
|
+
field: field.name,
|
|
186
|
+
reason: field.reason
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (failures.length > 0) {
|
|
192
|
+
process.stderr.write(
|
|
193
|
+
`validate-spoke: ${failures.length} canonical schema violation(s) in ${schemaPath}:
|
|
194
|
+
`
|
|
195
|
+
);
|
|
196
|
+
for (const f of failures) {
|
|
197
|
+
if (f.kind === "missing_model") {
|
|
198
|
+
process.stderr.write(` missing model: ${f.model}
|
|
199
|
+
`);
|
|
200
|
+
} else {
|
|
201
|
+
process.stderr.write(
|
|
202
|
+
` ${f.model}.${f.field} missing (required by: ${f.reason})
|
|
203
|
+
`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
process.stderr.write(
|
|
208
|
+
"\nThe app.manifest.json features list expects the listed canonical fields.\nEither add them to prisma/schema.prisma or turn the feature off in the\nmanifest. See @augmenting-integrations/deploy-tools README for the contract.\n"
|
|
209
|
+
);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
process.stdout.write(
|
|
213
|
+
`validate-spoke: OK -- ${manifest.appSlug} schema satisfies the canonical contract for features: ` + Object.entries(manifest.features).filter(([, v]) => v).map(([k]) => k).join(", ") + "\n"
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
var isDirectInvocation = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("validate-spoke.js");
|
|
217
|
+
if (isDirectInvocation) {
|
|
218
|
+
runValidateSpoke(process.argv.slice(2)).catch((err) => {
|
|
219
|
+
process.stderr.write(
|
|
220
|
+
`validate-spoke: ${err instanceof Error ? err.message : String(err)}
|
|
221
|
+
`
|
|
222
|
+
);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
runValidateSpoke
|
|
229
|
+
};
|
|
230
|
+
//# sourceMappingURL=chunk-A7IIB34G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validate-spoke.ts","../src/canonical/schema.ts","../src/prisma-parser.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"node:fs\";\nimport { resolve, join } from \"node:path\";\nimport process from \"node:process\";\n\nimport { loadManifest } from \"@augmenting-integrations/platform/manifest\";\n\nimport { requiredModelsForManifest } from \"./canonical/schema.js\";\nimport { parsePrismaSchema } from \"./prisma-parser.js\";\n\n// =============================================================================\n// `augint validate-spoke`\n//\n// Inputs:\n// - app.manifest.json (validated by platform/manifest loader)\n// - prisma/schema.prisma (parsed locally)\n//\n// Output:\n// - Exit 0 on success with a one-line \"OK\" summary.\n// - Exit 1 on validation failure with a field-by-field list of what's\n// missing and WHY each missing field is required by the library\n// handlers it serves.\n//\n// Apex apps with `dataPlane.type === \"none\"` skip Prisma validation\n// entirely (no schema file expected). Spokes always validate.\n// =============================================================================\n\nexport async function runValidateSpoke(argv: string[]): Promise<void> {\n let cwd = process.cwd();\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === \"--cwd\" || a === \"--root\") {\n const next = argv[i + 1];\n if (next) {\n cwd = resolve(next);\n i++;\n }\n }\n }\n\n let manifest: ReturnType<typeof loadManifest>;\n try {\n manifest = loadManifest({ cwd });\n } catch (err) {\n process.stderr.write(`validate-spoke: ${(err as Error).message}\\n`);\n process.exit(1);\n }\n\n // Apex auth-broker has no data plane -- nothing to validate.\n if (manifest.dataPlane.type === \"none\") {\n process.stdout.write(\n `validate-spoke: ${manifest.appSlug} has dataPlane.type=none; nothing to validate.\\n`,\n );\n return;\n }\n\n const schemaPath = join(cwd, \"prisma\", \"schema.prisma\");\n if (!existsSync(schemaPath)) {\n process.stderr.write(\n `validate-spoke: prisma/schema.prisma not found at ${schemaPath}\\n` +\n \"Manifest declares a data plane but the schema file is missing.\\n\",\n );\n process.exit(1);\n }\n\n const schemaSrc = readFileSync(schemaPath, \"utf8\");\n const parsed = parsePrismaSchema(schemaSrc);\n const required = requiredModelsForManifest(manifest);\n\n type Failure =\n | { kind: \"missing_model\"; model: string }\n | { kind: \"missing_field\"; model: string; field: string; reason: string };\n const failures: Failure[] = [];\n\n for (const req of required) {\n const found = parsed.find((m) => m.name === req.name);\n if (!found) {\n failures.push({ kind: \"missing_model\", model: req.name });\n continue;\n }\n const has = new Set(found.fields);\n for (const field of req.fields) {\n if (!has.has(field.name)) {\n failures.push({\n kind: \"missing_field\",\n model: req.name,\n field: field.name,\n reason: field.reason,\n });\n }\n }\n }\n\n if (failures.length > 0) {\n process.stderr.write(\n `validate-spoke: ${failures.length} canonical schema violation(s) in ${schemaPath}:\\n`,\n );\n for (const f of failures) {\n if (f.kind === \"missing_model\") {\n process.stderr.write(` missing model: ${f.model}\\n`);\n } else {\n process.stderr.write(\n ` ${f.model}.${f.field} missing (required by: ${f.reason})\\n`,\n );\n }\n }\n process.stderr.write(\n \"\\nThe app.manifest.json features list expects the listed canonical fields.\\n\" +\n \"Either add them to prisma/schema.prisma or turn the feature off in the\\n\" +\n \"manifest. See @augmenting-integrations/deploy-tools README for the contract.\\n\",\n );\n process.exit(1);\n }\n\n process.stdout.write(\n `validate-spoke: OK -- ${manifest.appSlug} schema satisfies the canonical contract for features: ` +\n Object.entries(manifest.features)\n .filter(([, v]) => v)\n .map(([k]) => k)\n .join(\", \") +\n \"\\n\",\n );\n}\n\nconst isDirectInvocation =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith(\"validate-spoke.js\");\nif (isDirectInvocation) {\n runValidateSpoke(process.argv.slice(2)).catch((err) => {\n process.stderr.write(\n `validate-spoke: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n}\n","// =============================================================================\n// Canonical schema contract.\n//\n// Encodes the Prisma model + field names that the library handler factories\n// read at runtime. The validator (`augint validate-spoke`) parses the\n// spoke's prisma/schema.prisma and asserts that every required model + field\n// for the manifest's enabled features is present by name.\n//\n// Drift in canonical naming is the most common class of integration bug\n// (e.g. `password` vs `password_hash`). The validator catches it before\n// `next build` runs.\n// =============================================================================\n\nimport type { AppManifest } from \"@augmenting-integrations/platform/manifest\";\n\nexport type RequiredField = {\n name: string;\n /**\n * Reason this field is required (shown in the error message). Helps\n * developers understand WHY the validator cares, not just WHAT it\n * found missing.\n */\n reason: string;\n};\n\nexport type RequiredModel = {\n name: string;\n fields: RequiredField[];\n};\n\n/**\n * Returns the list of required models for a given manifest, in priority\n * order. Each model lists the fields its library consumers read.\n *\n * `User` is always required.\n * `Invitation` is required when features.invitations.\n * `PaymentMethod` + `CreditTransaction` are required when features.billing.\n * `ActivityLog` is required when features.impersonation or features.invitations.\n */\nexport function requiredModelsForManifest(manifest: AppManifest): RequiredModel[] {\n const out: RequiredModel[] = [];\n\n out.push({\n name: \"User\",\n fields: [\n { name: \"id\", reason: \"JIT lookup + impersonation token claims\" },\n { name: \"email\", reason: \"JIT email lookup + auto-promote-to-admin\" },\n { name: \"name\", reason: \"/api/auth/me, UserMenu display\" },\n { name: \"role\", reason: \"JIT role resolution + admin gating\" },\n { name: \"is_active\", reason: \"/api/auth/me response shape\" },\n { name: \"credit_balance\", reason: \"JIT initial balance + /api/auth/me\" },\n { name: \"password\", reason: \"schema-inherited not-null filler at JIT create\" },\n { name: \"parent_id\", reason: \"invitation auto-accept sets parent_id from invite\" },\n { name: \"created_at\", reason: \"audit / ordering\" },\n { name: \"updated_at\", reason: \"audit / ordering\" },\n ],\n });\n\n if (manifest.features.billing) {\n out[0]!.fields.push(\n { name: \"stripe_customer_id\", reason: \"createBillingHandlers user shape\" },\n {\n name: \"stripe_default_payment_method\",\n reason: \"createBillingHandlers user shape\",\n },\n {\n name: \"stripe_default_payment_method_display\",\n reason: \"createBillingHandlers user shape\",\n },\n { name: \"auto_recharge_enabled\", reason: \"/api/billing/auto-recharge\" },\n { name: \"auto_recharge_threshold\", reason: \"/api/billing/auto-recharge\" },\n { name: \"auto_recharge_amount\", reason: \"/api/billing/auto-recharge\" },\n );\n\n out.push({\n name: \"PaymentMethod\",\n fields: [\n { name: \"id\", reason: \"createBillingHandlers row shape\" },\n { name: \"user_id\", reason: \"createBillingHandlers row shape\" },\n {\n name: \"stripe_payment_method_id\",\n reason: \"createBillingHandlers row shape (unique key)\",\n },\n { name: \"brand\", reason: \"createBillingHandlers row shape\" },\n { name: \"last4\", reason: \"createBillingHandlers row shape\" },\n { name: \"exp_month\", reason: \"createBillingHandlers row shape\" },\n { name: \"exp_year\", reason: \"createBillingHandlers row shape\" },\n { name: \"is_default\", reason: \"createBillingHandlers row shape\" },\n { name: \"created_at\", reason: \"createBillingHandlers row shape\" },\n ],\n });\n out.push({\n name: \"CreditTransaction\",\n fields: [\n { name: \"id\", reason: \"createBillingHandlers row shape\" },\n { name: \"user_id\", reason: \"createBillingHandlers row shape\" },\n { name: \"type\", reason: \"createBillingHandlers row shape\" },\n { name: \"amount\", reason: \"createBillingHandlers row shape\" },\n { name: \"description\", reason: \"createBillingHandlers row shape\" },\n {\n name: \"stripe_payment_intent_id\",\n reason: \"createBillingHandlers webhook idempotency\",\n },\n { name: \"payment_method_display\", reason: \"createBillingHandlers row shape\" },\n { name: \"created_at\", reason: \"createBillingHandlers row shape\" },\n ],\n });\n }\n\n if (manifest.features.invitations) {\n out.push({\n name: \"Invitation\",\n fields: [\n { name: \"id\", reason: \"createInvitationHandlers + JIT invitation accept\" },\n { name: \"token\", reason: \"createInvitationHandlers findUnique\" },\n { name: \"email\", reason: \"JIT invitation lookup\" },\n { name: \"intended_role\", reason: \"JIT role inheritance\" },\n { name: \"parent_id\", reason: \"JIT parent_id inheritance\" },\n { name: \"expires_at\", reason: \"JIT expiry filter\" },\n { name: \"accepted_at\", reason: \"JIT acceptance toggle\" },\n {\n name: \"accepted_by_user_id\",\n reason: \"JIT writes acceptance with new user id\",\n },\n { name: \"created_at\", reason: \"ordering\" },\n ],\n });\n }\n\n if (manifest.features.impersonation || manifest.features.invitations) {\n out.push({\n name: \"ActivityLog\",\n fields: [\n { name: \"id\", reason: \"audit row\" },\n { name: \"user_id\", reason: \"createImpersonateHandlers audit writes\" },\n { name: \"action\", reason: \"createImpersonateHandlers audit writes\" },\n { name: \"metadata\", reason: \"createImpersonateHandlers audit writes\" },\n { name: \"created_at\", reason: \"audit row\" },\n ],\n });\n }\n\n return out;\n}\n","// =============================================================================\n// Minimal Prisma schema parser. Extracts model names and the field names\n// declared inside each model body. Sufficient to validate that the spoke's\n// schema has the canonical models + fields the library handler factories\n// read.\n//\n// We deliberately do NOT validate Prisma TYPES (BigInt vs Int, Decimal vs\n// Int, etc.). The platform adapter in src/platform/repositories.ts widens\n// to structural shapes; runtime correctness depends on the canonical names\n// being present. Type drift is caught by the spoke's own `tsc` pass when\n// it tries to construct adapters against an actual generated client.\n// =============================================================================\n\nexport type ParsedModel = {\n name: string;\n fields: string[];\n};\n\n/**\n * Parse a Prisma schema file. Returns one ParsedModel per `model X { ... }`\n * block in declaration order. Field names are extracted from the start of\n * each non-blank, non-comment, non-attribute line inside the body.\n */\nexport function parsePrismaSchema(source: string): ParsedModel[] {\n const out: ParsedModel[] = [];\n const stripped = source.replace(/\\/\\/[^\\n]*/g, \"\"); // line comments\n const modelRegex = /model\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\{([\\s\\S]*?)\\}/g;\n for (const match of stripped.matchAll(modelRegex)) {\n const name = match[1]!;\n const body = match[2]!;\n const fields: string[] = [];\n for (const rawLine of body.split(\"\\n\")) {\n const line = rawLine.trim();\n if (line === \"\") continue;\n if (line.startsWith(\"@@\")) continue;\n if (line.startsWith(\"@\")) continue;\n const fieldMatch = /^([A-Za-z_][A-Za-z0-9_]*)\\s+/.exec(line);\n if (fieldMatch) fields.push(fieldMatch[1]!);\n }\n out.push({ name, fields });\n }\n return out;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/package-next-lambda.ts
|
|
4
|
+
import {
|
|
5
|
+
cpSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
lstatSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readdirSync,
|
|
10
|
+
readlinkSync,
|
|
11
|
+
realpathSync,
|
|
12
|
+
rmSync,
|
|
13
|
+
statSync,
|
|
14
|
+
chmodSync
|
|
15
|
+
} from "fs";
|
|
16
|
+
import { dirname, join, resolve } from "path";
|
|
17
|
+
import process from "process";
|
|
18
|
+
function parseArgs(argv) {
|
|
19
|
+
let cwd = process.cwd();
|
|
20
|
+
let prismaHoist = true;
|
|
21
|
+
let stageMakefile = true;
|
|
22
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23
|
+
const a = argv[i];
|
|
24
|
+
if (a === "--cwd" || a === "--root") {
|
|
25
|
+
const next = argv[i + 1];
|
|
26
|
+
if (next) {
|
|
27
|
+
cwd = resolve(next);
|
|
28
|
+
i++;
|
|
29
|
+
}
|
|
30
|
+
} else if (a === "--no-prisma") {
|
|
31
|
+
prismaHoist = false;
|
|
32
|
+
} else if (a === "--no-makefile") {
|
|
33
|
+
stageMakefile = false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { cwd, prismaHoist, stageMakefile };
|
|
37
|
+
}
|
|
38
|
+
function copyIfExists(src, dst, opts = {}) {
|
|
39
|
+
if (!existsSync(src)) return;
|
|
40
|
+
cpSync(src, dst, { recursive: true, dereference: !!opts.dereference, force: true });
|
|
41
|
+
}
|
|
42
|
+
function* walkSymlinks(root) {
|
|
43
|
+
if (!existsSync(root)) return;
|
|
44
|
+
const stack = [root];
|
|
45
|
+
while (stack.length > 0) {
|
|
46
|
+
const dir = stack.pop();
|
|
47
|
+
let entries;
|
|
48
|
+
try {
|
|
49
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
50
|
+
} catch {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
for (const e of entries) {
|
|
54
|
+
const full = join(dir, e.name);
|
|
55
|
+
let lst;
|
|
56
|
+
try {
|
|
57
|
+
lst = lstatSync(full);
|
|
58
|
+
} catch {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (lst.isSymbolicLink()) {
|
|
62
|
+
yield full;
|
|
63
|
+
} else if (lst.isDirectory()) {
|
|
64
|
+
stack.push(full);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function flattenSymlinks(roots) {
|
|
70
|
+
for (let pass = 0; pass < 5; pass++) {
|
|
71
|
+
let flattened = 0;
|
|
72
|
+
for (const root of roots) {
|
|
73
|
+
for (const link of walkSymlinks(root)) {
|
|
74
|
+
let target;
|
|
75
|
+
try {
|
|
76
|
+
target = realpathSync(link);
|
|
77
|
+
} catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!existsSync(target)) continue;
|
|
81
|
+
rmSync(link, { force: true });
|
|
82
|
+
cpSync(target, link, { recursive: true, dereference: true, force: true });
|
|
83
|
+
flattened++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (flattened === 0) return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function copyRuntimePkg(cwd, pkg, standaloneNodeMods) {
|
|
90
|
+
const candidates = [
|
|
91
|
+
join(cwd, "node_modules", pkg),
|
|
92
|
+
join(cwd, "node_modules", ".pnpm", "node_modules", pkg)
|
|
93
|
+
];
|
|
94
|
+
for (const src of candidates) {
|
|
95
|
+
if (existsSync(src)) {
|
|
96
|
+
const dst = join(standaloneNodeMods, pkg);
|
|
97
|
+
rmSync(dst, { recursive: true, force: true });
|
|
98
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
99
|
+
cpSync(src, dst, { recursive: true, dereference: true, force: true });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw new Error(`runtime package not found: ${pkg}`);
|
|
104
|
+
}
|
|
105
|
+
function hoistPrismaClient(standaloneNodeMods) {
|
|
106
|
+
const pnpmRoot = join(standaloneNodeMods, ".pnpm");
|
|
107
|
+
if (!existsSync(pnpmRoot)) return;
|
|
108
|
+
const found = findFirst(
|
|
109
|
+
pnpmRoot,
|
|
110
|
+
(full) => full.endsWith(join("node_modules", ".prisma", "client"))
|
|
111
|
+
);
|
|
112
|
+
if (!found) return;
|
|
113
|
+
const dst = join(standaloneNodeMods, ".prisma", "client");
|
|
114
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
115
|
+
cpSync(found, dst, { recursive: true, dereference: true, force: true });
|
|
116
|
+
}
|
|
117
|
+
function findFirst(root, predicate) {
|
|
118
|
+
const stack = [root];
|
|
119
|
+
while (stack.length > 0) {
|
|
120
|
+
const dir = stack.pop();
|
|
121
|
+
let entries;
|
|
122
|
+
try {
|
|
123
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
124
|
+
} catch {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
for (const e of entries) {
|
|
128
|
+
const full = join(dir, e.name);
|
|
129
|
+
if (predicate(full)) return full;
|
|
130
|
+
if (e.isDirectory()) stack.push(full);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
async function runPackageNextLambda(argv) {
|
|
136
|
+
const opts = parseArgs(argv);
|
|
137
|
+
const cwd = opts.cwd;
|
|
138
|
+
const standalone = join(cwd, ".next", "standalone");
|
|
139
|
+
if (!existsSync(standalone)) {
|
|
140
|
+
process.stderr.write(
|
|
141
|
+
`package-next-lambda: ${standalone} missing. Run \`next build\` first.
|
|
142
|
+
`
|
|
143
|
+
);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
mkdirSync(join(standalone, ".next"), { recursive: true });
|
|
147
|
+
copyIfExists(join(cwd, ".next", "static"), join(standalone, ".next", "static"));
|
|
148
|
+
copyIfExists(join(cwd, "public"), join(standalone, "public"));
|
|
149
|
+
copyIfExists(join(cwd, "content"), join(standalone, "content"));
|
|
150
|
+
copyIfExists(join(cwd, "app.manifest.json"), join(standalone, "app.manifest.json"));
|
|
151
|
+
const runSh = join(cwd, "run.sh");
|
|
152
|
+
if (existsSync(runSh)) {
|
|
153
|
+
cpSync(runSh, join(standalone, "run.sh"));
|
|
154
|
+
try {
|
|
155
|
+
chmodSync(join(standalone, "run.sh"), 493);
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (opts.stageMakefile) {
|
|
160
|
+
copyIfExists(join(cwd, "Makefile"), join(standalone, "Makefile"));
|
|
161
|
+
}
|
|
162
|
+
flattenSymlinks([
|
|
163
|
+
join(standalone, "node_modules"),
|
|
164
|
+
join(standalone, ".next", "node_modules")
|
|
165
|
+
]);
|
|
166
|
+
const standaloneNodeMods = join(standalone, "node_modules");
|
|
167
|
+
for (const pkg of ["@swc/helpers", "@next/env"]) {
|
|
168
|
+
try {
|
|
169
|
+
copyRuntimePkg(cwd, pkg, standaloneNodeMods);
|
|
170
|
+
} catch (err) {
|
|
171
|
+
process.stderr.write(
|
|
172
|
+
`package-next-lambda: ${err.message}
|
|
173
|
+
Consider adding the package to dependencies or removing it from the runtime list.
|
|
174
|
+
`
|
|
175
|
+
);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (opts.prismaHoist) {
|
|
180
|
+
hoistPrismaClient(standaloneNodeMods);
|
|
181
|
+
}
|
|
182
|
+
const pnpmRoot = join(standaloneNodeMods, ".pnpm");
|
|
183
|
+
if (existsSync(pnpmRoot)) {
|
|
184
|
+
rmSync(pnpmRoot, { recursive: true, force: true });
|
|
185
|
+
}
|
|
186
|
+
const bytes = directorySize(standalone);
|
|
187
|
+
process.stdout.write(
|
|
188
|
+
`package-next-lambda: staged ${standalone} (${(bytes / 1024 / 1024).toFixed(1)} MB)
|
|
189
|
+
`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
function directorySize(root) {
|
|
193
|
+
let total = 0;
|
|
194
|
+
const stack = [root];
|
|
195
|
+
while (stack.length > 0) {
|
|
196
|
+
const dir = stack.pop();
|
|
197
|
+
let entries;
|
|
198
|
+
try {
|
|
199
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
200
|
+
} catch {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
for (const e of entries) {
|
|
204
|
+
const full = join(dir, e.name);
|
|
205
|
+
try {
|
|
206
|
+
if (e.isDirectory()) {
|
|
207
|
+
stack.push(full);
|
|
208
|
+
} else {
|
|
209
|
+
total += statSync(full).size;
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return total;
|
|
216
|
+
}
|
|
217
|
+
var isDirectInvocation = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("package-next-lambda.js");
|
|
218
|
+
if (isDirectInvocation) {
|
|
219
|
+
runPackageNextLambda(process.argv.slice(2)).catch((err) => {
|
|
220
|
+
process.stderr.write(
|
|
221
|
+
`package-next-lambda: ${err instanceof Error ? err.message : String(err)}
|
|
222
|
+
`
|
|
223
|
+
);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export {
|
|
229
|
+
runPackageNextLambda
|
|
230
|
+
};
|
|
231
|
+
//# sourceMappingURL=chunk-Q5W2NBVE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/package-next-lambda.ts"],"sourcesContent":["import {\n cpSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readlinkSync,\n realpathSync,\n rmSync,\n statSync,\n chmodSync,\n} from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport process from \"node:process\";\n\n// =============================================================================\n// `augint package-next-lambda`\n//\n// Stages a Next.js standalone build into a Lambda-ready directory at\n// `.next/standalone/`. Lifts the symlink-flatten loop, force-copy of\n// runtime-required pnpm packages, Prisma .prisma hoist, and .pnpm purge\n// from each spoke's CI workflow so the logic is fixed in one place.\n//\n// Defaults assume the spoke convention:\n// - cwd contains .next/ (from `next build`)\n// - run.sh at cwd root (LWA entry)\n// - optional Makefile at cwd root (SAM BuildMethod=makefile)\n// - optional public/ and content/ at cwd root\n//\n// Flags:\n// --cwd <path> Override working directory\n// --no-prisma Skip Prisma .prisma hoist (use for apex)\n// --no-makefile Skip Makefile stage\n// =============================================================================\n\ntype Options = {\n cwd: string;\n prismaHoist: boolean;\n stageMakefile: boolean;\n};\n\nfunction parseArgs(argv: string[]): Options {\n let cwd = process.cwd();\n let prismaHoist = true;\n let stageMakefile = true;\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === \"--cwd\" || a === \"--root\") {\n const next = argv[i + 1];\n if (next) {\n cwd = resolve(next);\n i++;\n }\n } else if (a === \"--no-prisma\") {\n prismaHoist = false;\n } else if (a === \"--no-makefile\") {\n stageMakefile = false;\n }\n }\n return { cwd, prismaHoist, stageMakefile };\n}\n\nfunction copyIfExists(src: string, dst: string, opts: { dereference?: boolean } = {}) {\n if (!existsSync(src)) return;\n cpSync(src, dst, { recursive: true, dereference: !!opts.dereference, force: true });\n}\n\nfunction* walkSymlinks(root: string): Generator<string> {\n if (!existsSync(root)) return;\n const stack = [root];\n while (stack.length > 0) {\n const dir = stack.pop()!;\n let entries: Array<{ name: string; isDirectory(): boolean; isSymbolicLink(): boolean }>;\n try {\n entries = readdirSync(dir, { withFileTypes: true }) as unknown as Array<{\n name: string;\n isDirectory(): boolean;\n isSymbolicLink(): boolean;\n }>;\n } catch {\n continue;\n }\n for (const e of entries) {\n const full = join(dir, e.name);\n let lst: ReturnType<typeof lstatSync>;\n try {\n lst = lstatSync(full);\n } catch {\n continue;\n }\n if (lst.isSymbolicLink()) {\n yield full;\n } else if (lst.isDirectory()) {\n stack.push(full);\n }\n }\n }\n}\n\nfunction flattenSymlinks(roots: string[]) {\n // Multiple passes: dereferencing one link can expose a deeper one.\n for (let pass = 0; pass < 5; pass++) {\n let flattened = 0;\n for (const root of roots) {\n for (const link of walkSymlinks(root)) {\n let target: string;\n try {\n target = realpathSync(link);\n } catch {\n continue;\n }\n if (!existsSync(target)) continue;\n rmSync(link, { force: true });\n cpSync(target, link, { recursive: true, dereference: true, force: true });\n flattened++;\n }\n }\n if (flattened === 0) return;\n }\n}\n\nfunction copyRuntimePkg(cwd: string, pkg: string, standaloneNodeMods: string) {\n // Look in the standard tree and the .pnpm/node_modules hoist position.\n const candidates = [\n join(cwd, \"node_modules\", pkg),\n join(cwd, \"node_modules\", \".pnpm\", \"node_modules\", pkg),\n ];\n for (const src of candidates) {\n if (existsSync(src)) {\n const dst = join(standaloneNodeMods, pkg);\n rmSync(dst, { recursive: true, force: true });\n mkdirSync(dirname(dst), { recursive: true });\n cpSync(src, dst, { recursive: true, dereference: true, force: true });\n return;\n }\n }\n throw new Error(`runtime package not found: ${pkg}`);\n}\n\nfunction hoistPrismaClient(standaloneNodeMods: string) {\n // pnpm keeps .prisma/client inside the .pnpm subdir alongside @prisma/client.\n // After we flatten and purge .pnpm, that path disappears, but @prisma/client\n // does require('.prisma/client/...') at runtime. Hoist a copy first.\n const pnpmRoot = join(standaloneNodeMods, \".pnpm\");\n if (!existsSync(pnpmRoot)) return;\n const found = findFirst(pnpmRoot, (full) =>\n full.endsWith(join(\"node_modules\", \".prisma\", \"client\")),\n );\n if (!found) return;\n const dst = join(standaloneNodeMods, \".prisma\", \"client\");\n mkdirSync(dirname(dst), { recursive: true });\n cpSync(found, dst, { recursive: true, dereference: true, force: true });\n}\n\nfunction findFirst(root: string, predicate: (path: string) => boolean): string | null {\n const stack = [root];\n while (stack.length > 0) {\n const dir = stack.pop()!;\n let entries: Array<{ name: string; isDirectory(): boolean; isSymbolicLink(): boolean }>;\n try {\n entries = readdirSync(dir, { withFileTypes: true }) as unknown as Array<{\n name: string;\n isDirectory(): boolean;\n isSymbolicLink(): boolean;\n }>;\n } catch {\n continue;\n }\n for (const e of entries) {\n const full = join(dir, e.name);\n if (predicate(full)) return full;\n if (e.isDirectory()) stack.push(full);\n }\n }\n return null;\n}\n\nexport async function runPackageNextLambda(argv: string[]): Promise<void> {\n const opts = parseArgs(argv);\n const cwd = opts.cwd;\n const standalone = join(cwd, \".next\", \"standalone\");\n if (!existsSync(standalone)) {\n process.stderr.write(\n `package-next-lambda: ${standalone} missing. Run \\`next build\\` first.\\n`,\n );\n process.exit(1);\n }\n\n // ----- Stage static + public + content + run.sh + Makefile + manifest -----\n mkdirSync(join(standalone, \".next\"), { recursive: true });\n copyIfExists(join(cwd, \".next\", \"static\"), join(standalone, \".next\", \"static\"));\n copyIfExists(join(cwd, \"public\"), join(standalone, \"public\"));\n copyIfExists(join(cwd, \"content\"), join(standalone, \"content\"));\n copyIfExists(join(cwd, \"app.manifest.json\"), join(standalone, \"app.manifest.json\"));\n const runSh = join(cwd, \"run.sh\");\n if (existsSync(runSh)) {\n cpSync(runSh, join(standalone, \"run.sh\"));\n try {\n chmodSync(join(standalone, \"run.sh\"), 0o755);\n } catch {\n // chmod may fail on Windows runners; ignore.\n }\n }\n if (opts.stageMakefile) {\n copyIfExists(join(cwd, \"Makefile\"), join(standalone, \"Makefile\"));\n }\n\n // ----- Flatten pnpm symlinks (Lambda runtime can't follow them) -----\n flattenSymlinks([\n join(standalone, \"node_modules\"),\n join(standalone, \".next\", \"node_modules\"),\n ]);\n\n // ----- Force-copy known runtime-required packages -----\n const standaloneNodeMods = join(standalone, \"node_modules\");\n for (const pkg of [\"@swc/helpers\", \"@next/env\"]) {\n try {\n copyRuntimePkg(cwd, pkg, standaloneNodeMods);\n } catch (err) {\n process.stderr.write(\n `package-next-lambda: ${(err as Error).message}\\n` +\n \"Consider adding the package to dependencies or removing it from the runtime list.\\n\",\n );\n process.exit(1);\n }\n }\n\n // ----- Prisma .prisma hoist (spokes with a data plane) -----\n if (opts.prismaHoist) {\n hoistPrismaClient(standaloneNodeMods);\n }\n\n // ----- Purge .pnpm (unreferenced after symlink flatten) -----\n const pnpmRoot = join(standaloneNodeMods, \".pnpm\");\n if (existsSync(pnpmRoot)) {\n rmSync(pnpmRoot, { recursive: true, force: true });\n }\n\n // ----- Report size -----\n const bytes = directorySize(standalone);\n process.stdout.write(\n `package-next-lambda: staged ${standalone} (${(bytes / 1024 / 1024).toFixed(1)} MB)\\n`,\n );\n}\n\nfunction directorySize(root: string): number {\n let total = 0;\n const stack = [root];\n while (stack.length > 0) {\n const dir = stack.pop()!;\n let entries: Array<{ name: string; isDirectory(): boolean; isSymbolicLink(): boolean }>;\n try {\n entries = readdirSync(dir, { withFileTypes: true }) as unknown as Array<{\n name: string;\n isDirectory(): boolean;\n isSymbolicLink(): boolean;\n }>;\n } catch {\n continue;\n }\n for (const e of entries) {\n const full = join(dir, e.name);\n try {\n if (e.isDirectory()) {\n stack.push(full);\n } else {\n total += statSync(full).size;\n }\n } catch {\n // ignore unreadable\n }\n }\n }\n return total;\n}\n\nvoid readlinkSync; // referenced for completeness in walk; silence unused warning\n\nconst isDirectInvocation =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith(\"package-next-lambda.js\");\nif (isDirectInvocation) {\n runPackageNextLambda(process.argv.slice(2)).catch((err) => {\n process.stderr.write(\n `package-next-lambda: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runValidateSpoke
|
|
4
|
+
} from "./chunk-A7IIB34G.js";
|
|
5
|
+
import {
|
|
6
|
+
runPackageNextLambda
|
|
7
|
+
} from "./chunk-Q5W2NBVE.js";
|
|
8
|
+
|
|
9
|
+
// src/cli.ts
|
|
10
|
+
import process from "process";
|
|
11
|
+
var USAGE = `Usage: augint <command> [args]
|
|
12
|
+
|
|
13
|
+
Commands:
|
|
14
|
+
validate-spoke Validate Prisma schema against canonical contract
|
|
15
|
+
package-next-lambda Package Next standalone build for Lambda
|
|
16
|
+
`;
|
|
17
|
+
async function main() {
|
|
18
|
+
const [, , cmd, ...rest] = process.argv;
|
|
19
|
+
if (!cmd || cmd === "--help" || cmd === "-h") {
|
|
20
|
+
process.stdout.write(USAGE);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
switch (cmd) {
|
|
24
|
+
case "validate-spoke":
|
|
25
|
+
await runValidateSpoke(rest);
|
|
26
|
+
return;
|
|
27
|
+
case "package-next-lambda":
|
|
28
|
+
await runPackageNextLambda(rest);
|
|
29
|
+
return;
|
|
30
|
+
default:
|
|
31
|
+
process.stderr.write(`augint: unknown command '${cmd}'
|
|
32
|
+
${USAGE}`);
|
|
33
|
+
process.exit(2);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
main().catch((err) => {
|
|
37
|
+
process.stderr.write(`augint: ${err instanceof Error ? err.message : String(err)}
|
|
38
|
+
`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import process from \"node:process\";\nimport { runValidateSpoke } from \"./validate-spoke.js\";\nimport { runPackageNextLambda } from \"./package-next-lambda.js\";\n\nconst USAGE = `Usage: augint <command> [args]\n\nCommands:\n validate-spoke Validate Prisma schema against canonical contract\n package-next-lambda Package Next standalone build for Lambda\n`;\n\nasync function main(): Promise<void> {\n const [, , cmd, ...rest] = process.argv;\n if (!cmd || cmd === \"--help\" || cmd === \"-h\") {\n process.stdout.write(USAGE);\n return;\n }\n switch (cmd) {\n case \"validate-spoke\":\n await runValidateSpoke(rest);\n return;\n case \"package-next-lambda\":\n await runPackageNextLambda(rest);\n return;\n default:\n process.stderr.write(`augint: unknown command '${cmd}'\\n${USAGE}`);\n process.exit(2);\n }\n}\n\nmain().catch((err) => {\n process.stderr.write(`augint: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;AAAA,OAAO,aAAa;AAIpB,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAOd,eAAe,OAAsB;AACnC,QAAM,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,IAAI,QAAQ;AACnC,MAAI,CAAC,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC5C,YAAQ,OAAO,MAAM,KAAK;AAC1B;AAAA,EACF;AACA,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,YAAM,iBAAiB,IAAI;AAC3B;AAAA,IACF,KAAK;AACH,YAAM,qBAAqB,IAAI;AAC/B;AAAA,IACF;AACE,cAAQ,OAAO,MAAM,4BAA4B,GAAG;AAAA,EAAM,KAAK,EAAE;AACjE,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,OAAO,MAAM,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AACpF,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-next-lambda.d.ts","sourceRoot":"","sources":["../src/package-next-lambda.ts"],"names":[],"mappings":"AAiLA,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkExE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ParsedModel = {
|
|
2
|
+
name: string;
|
|
3
|
+
fields: string[];
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Parse a Prisma schema file. Returns one ParsedModel per `model X { ... }`
|
|
7
|
+
* block in declaration order. Field names are extracted from the start of
|
|
8
|
+
* each non-blank, non-comment, non-attribute line inside the body.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parsePrismaSchema(source: string): ParsedModel[];
|
|
11
|
+
//# sourceMappingURL=prisma-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-parser.d.ts","sourceRoot":"","sources":["../src/prisma-parser.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAmB/D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-spoke.d.ts","sourceRoot":"","sources":["../src/validate-spoke.ts"],"names":[],"mappings":"AA0BA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+FpE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@augmenting-integrations/deploy-tools",
|
|
3
|
+
"version": "8.4.0",
|
|
4
|
+
"description": "Augint platform deploy tooling. `augint validate-spoke` checks Prisma canonical schema against the app manifest; `augint package-next-lambda` packages a Next standalone build for Lambda. Shared across every spoke + apex repo so deploy logic isn't copy/pasted into each app's CI workflow.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"bin": {
|
|
11
|
+
"augint": "./dist/cli.js",
|
|
12
|
+
"augint-validate-spoke": "./dist/validate-spoke.js",
|
|
13
|
+
"augint-package-next-lambda": "./dist/package-next-lambda.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"scripts",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"clean": "rm -rf dist",
|
|
23
|
+
"test": "vitest run --passWithNoTests"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@augmenting-integrations/platform": "workspace:*"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@augmenting-integrations/platform": "workspace:*",
|
|
30
|
+
"@types/node": "^20.17.6",
|
|
31
|
+
"tsup": "^8.3.5",
|
|
32
|
+
"typescript": "^5.7.2",
|
|
33
|
+
"vitest": "^4.1.5"
|
|
34
|
+
}
|
|
35
|
+
}
|