bonescript-compiler 0.6.0 → 0.6.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/dist/cli.js +1 -1
- package/dist/emit_nakama.js +36 -36
- package/dist/emit_notify.js +30 -2
- package/dist/emit_notify.js.map +1 -1
- package/dist/emit_runtime.js +80 -80
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/emit_nakama.ts +576 -576
- package/src/emit_notify.ts +30 -2
- package/src/emit_runtime.ts +955 -955
package/dist/cli.js
CHANGED
|
@@ -94,7 +94,7 @@ function main() {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
function showHelp() {
|
|
97
|
-
console.log("BoneScript compiler v0.6.
|
|
97
|
+
console.log("BoneScript compiler v0.6.1");
|
|
98
98
|
console.log("");
|
|
99
99
|
console.log("Usage:");
|
|
100
100
|
console.log(" bonec compile <file> [--target <target>] Compile to runnable project");
|
package/dist/emit_nakama.js
CHANGED
|
@@ -415,44 +415,44 @@ function emitNakamaReadme(system) {
|
|
|
415
415
|
}
|
|
416
416
|
}
|
|
417
417
|
const realtimeMods = system.modules.filter(m => m.kind === "realtime_service");
|
|
418
|
-
return `# ${system.name} — Nakama Backend
|
|
419
|
-
|
|
420
|
-
Generated by BoneScript compiler (target: nakama). Source hash: ${system.source_hash}
|
|
421
|
-
|
|
422
|
-
## Quick Start
|
|
423
|
-
|
|
424
|
-
\`\`\`bash
|
|
425
|
-
# Install deps
|
|
426
|
-
npm install
|
|
427
|
-
|
|
428
|
-
# Build TypeScript -> JS
|
|
429
|
-
npm run build
|
|
430
|
-
|
|
431
|
-
# Start Nakama with this module
|
|
432
|
-
docker run --rm -v $(pwd)/build:/nakama/data/modules \\
|
|
433
|
-
heroiclabs/nakama:latest
|
|
434
|
-
\`\`\`
|
|
435
|
-
|
|
436
|
-
## RPC Endpoints
|
|
437
|
-
|
|
438
|
-
Call these from any Nakama client SDK:
|
|
439
|
-
|
|
440
|
-
${rpcs.join("\n")}
|
|
441
|
-
|
|
442
|
-
## Match Handlers
|
|
443
|
-
|
|
418
|
+
return `# ${system.name} — Nakama Backend
|
|
419
|
+
|
|
420
|
+
Generated by BoneScript compiler (target: nakama). Source hash: ${system.source_hash}
|
|
421
|
+
|
|
422
|
+
## Quick Start
|
|
423
|
+
|
|
424
|
+
\`\`\`bash
|
|
425
|
+
# Install deps
|
|
426
|
+
npm install
|
|
427
|
+
|
|
428
|
+
# Build TypeScript -> JS
|
|
429
|
+
npm run build
|
|
430
|
+
|
|
431
|
+
# Start Nakama with this module
|
|
432
|
+
docker run --rm -v $(pwd)/build:/nakama/data/modules \\
|
|
433
|
+
heroiclabs/nakama:latest
|
|
434
|
+
\`\`\`
|
|
435
|
+
|
|
436
|
+
## RPC Endpoints
|
|
437
|
+
|
|
438
|
+
Call these from any Nakama client SDK:
|
|
439
|
+
|
|
440
|
+
${rpcs.join("\n")}
|
|
441
|
+
|
|
442
|
+
## Match Handlers
|
|
443
|
+
|
|
444
444
|
${realtimeMods.length > 0
|
|
445
445
|
? realtimeMods.map(m => `- \`${toSnakeCase(m.name)}\` (tick rate: ${m.config["tick_rate"] ?? 10}/s)`).join("\n")
|
|
446
|
-
: "_No realtime channels declared._"}
|
|
447
|
-
|
|
448
|
-
## Storage Collections
|
|
449
|
-
|
|
450
|
-
${system.modules.flatMap(m => m.models).map(m => `- \`${toSnakeCase(m.name)}s\``).join("\n") || "_No entities declared._"}
|
|
451
|
-
|
|
452
|
-
## Auth
|
|
453
|
-
|
|
454
|
-
All authenticated RPCs require a valid Nakama session token.
|
|
455
|
-
Use any Nakama client SDK to authenticate before calling RPCs.
|
|
446
|
+
: "_No realtime channels declared._"}
|
|
447
|
+
|
|
448
|
+
## Storage Collections
|
|
449
|
+
|
|
450
|
+
${system.modules.flatMap(m => m.models).map(m => `- \`${toSnakeCase(m.name)}s\``).join("\n") || "_No entities declared._"}
|
|
451
|
+
|
|
452
|
+
## Auth
|
|
453
|
+
|
|
454
|
+
All authenticated RPCs require a valid Nakama session token.
|
|
455
|
+
Use any Nakama client SDK to authenticate before calling RPCs.
|
|
456
456
|
`;
|
|
457
457
|
}
|
|
458
458
|
class NakamaEmitter {
|
package/dist/emit_notify.js
CHANGED
|
@@ -29,7 +29,32 @@ function emitNotifyService(system) {
|
|
|
29
29
|
lines.push(` body: string;`);
|
|
30
30
|
lines.push(`}`);
|
|
31
31
|
lines.push(``);
|
|
32
|
+
// HTML-escape helper. Used to render arbitrary event payload data inside an
|
|
33
|
+
// HTML email body without enabling HTML/JS injection in clients that render
|
|
34
|
+
// it. Subject lines are also escaped for consistency.
|
|
35
|
+
lines.push(`function escapeHtml(s: string): string {`);
|
|
36
|
+
lines.push(` return s`);
|
|
37
|
+
lines.push(` .replace(/&/g, "&")`);
|
|
38
|
+
lines.push(` .replace(/</g, "<")`);
|
|
39
|
+
lines.push(` .replace(/>/g, ">")`);
|
|
40
|
+
lines.push(` .replace(/"/g, """)`);
|
|
41
|
+
lines.push(` .replace(/'/g, "'");`);
|
|
42
|
+
lines.push(`}`);
|
|
43
|
+
lines.push(``);
|
|
44
|
+
// Conservative email validation. Rejects empty input, multiple addresses,
|
|
45
|
+
// and CRLF that would let a caller inject extra headers into the request body
|
|
46
|
+
// sent to Resend / SendGrid.
|
|
47
|
+
lines.push(`const __EMAIL_RE = /^[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}$/;`);
|
|
48
|
+
lines.push(`function isValidEmail(addr: string): boolean {`);
|
|
49
|
+
lines.push(` if (!addr || addr.length > 254) return false;`);
|
|
50
|
+
lines.push(` if (/[\\r\\n]/.test(addr)) return false;`);
|
|
51
|
+
lines.push(` return __EMAIL_RE.test(addr);`);
|
|
52
|
+
lines.push(`}`);
|
|
53
|
+
lines.push(``);
|
|
32
54
|
lines.push(`async function sendEmail(msg: NotifyMessage): Promise<void> {`);
|
|
55
|
+
lines.push(` if (!isValidEmail(msg.to)) {`);
|
|
56
|
+
lines.push(" throw new Error(`Invalid recipient address: ${msg.to.slice(0, 64)}`);");
|
|
57
|
+
lines.push(` }`);
|
|
33
58
|
lines.push(` if (PROVIDER === "log") {`);
|
|
34
59
|
lines.push(` console.log(\`[notify] \${msg.subject} → \${msg.to}\`);`);
|
|
35
60
|
lines.push(` return;`);
|
|
@@ -54,15 +79,18 @@ function emitNotifyService(system) {
|
|
|
54
79
|
lines.push(` }`);
|
|
55
80
|
lines.push(`}`);
|
|
56
81
|
lines.push(``);
|
|
57
|
-
// Per-event handlers
|
|
82
|
+
// Per-event handlers — payload is JSON-stringified and HTML-escaped before
|
|
83
|
+
// being placed inside <pre> so payload values like "<script>..." can't break
|
|
84
|
+
// out of the body.
|
|
58
85
|
for (const event of system.events) {
|
|
59
86
|
const eventName = toPascalCase(event.name);
|
|
60
87
|
lines.push(`// Handler for ${eventName}`);
|
|
61
88
|
lines.push(`export async function notify${eventName}(event: SystemEvent, recipientEmail: string): Promise<void> {`);
|
|
89
|
+
lines.push(` const __payload = escapeHtml(JSON.stringify(event.payload, null, 2));`);
|
|
62
90
|
lines.push(` await sendEmail({`);
|
|
63
91
|
lines.push(` to: recipientEmail,`);
|
|
64
92
|
lines.push(` subject: "${eventName} notification",`);
|
|
65
|
-
lines.push(
|
|
93
|
+
lines.push(" body: `<p>Event <strong>${escapeHtml(\"" + eventName + "\")}</strong> occurred.</p><pre>${__payload}</pre>`,");
|
|
66
94
|
lines.push(` });`);
|
|
67
95
|
lines.push(`}`);
|
|
68
96
|
lines.push(``);
|
package/dist/emit_notify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit_notify.js","sourceRoot":"","sources":["../src/emit_notify.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"emit_notify.js","sourceRoot":"","sources":["../src/emit_notify.ts"],"names":[],"mappings":"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"}
|
package/dist/emit_runtime.js
CHANGED
|
@@ -140,86 +140,86 @@ function emitDbClient(system) {
|
|
|
140
140
|
}
|
|
141
141
|
exports.emitDbClient = emitDbClient;
|
|
142
142
|
function emitAuthMiddleware(system) {
|
|
143
|
-
return `// Generated by BoneScript compiler. DO NOT EDIT.
|
|
144
|
-
import { Request, Response, NextFunction } from "express";
|
|
145
|
-
import jwt from "jsonwebtoken";
|
|
146
|
-
import { v4 as uuid } from "uuid";
|
|
147
|
-
|
|
148
|
-
// JWT_SECRET must be set in production. The server will refuse to start without it
|
|
149
|
-
// when NODE_ENV is "production" to prevent accidental use of a weak fallback.
|
|
150
|
-
const JWT_SECRET = (() => {
|
|
151
|
-
const secret = process.env.JWT_SECRET;
|
|
152
|
-
if (!secret) {
|
|
153
|
-
if (process.env.NODE_ENV === "production") {
|
|
154
|
-
console.error("[FATAL] JWT_SECRET environment variable is not set. Refusing to start in production.");
|
|
155
|
-
process.exit(1);
|
|
156
|
-
}
|
|
157
|
-
console.warn("[WARN] JWT_SECRET is not set. Using insecure default — do not use in production.");
|
|
158
|
-
return "bonescript-dev-secret-do-not-use-in-production";
|
|
159
|
-
}
|
|
160
|
-
if (secret.length < 32) {
|
|
161
|
-
console.warn("[WARN] JWT_SECRET is shorter than 32 characters. Use a longer secret in production.");
|
|
162
|
-
}
|
|
163
|
-
return secret;
|
|
164
|
-
})();
|
|
165
|
-
|
|
166
|
-
export interface AuthContext {
|
|
167
|
-
authenticated: boolean;
|
|
168
|
-
actor_id: string | null;
|
|
169
|
-
trace_id: string;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Trace IDs are server-generated. We accept a client-supplied X-Trace-Id only
|
|
173
|
-
// if it parses as a UUID, so callers can correlate their own logs without
|
|
174
|
-
// being able to forge correlation IDs in audit / event records.
|
|
175
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
176
|
-
function resolveTraceId(req: Request): string {
|
|
177
|
-
const supplied = req.headers["x-trace-id"];
|
|
178
|
-
if (typeof supplied === "string" && UUID_RE.test(supplied)) return supplied;
|
|
179
|
-
return uuid();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
183
|
-
const header = req.headers.authorization;
|
|
184
|
-
const traceId = resolveTraceId(req);
|
|
185
|
-
if (!header || !header.startsWith("Bearer ")) {
|
|
186
|
-
(req as any).auth = { authenticated: false, actor_id: null, trace_id: traceId };
|
|
187
|
-
next();
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
try {
|
|
191
|
-
const token = header.slice(7);
|
|
192
|
-
// Pin algorithms to HS256 and require an expiration. Without pinning,
|
|
193
|
-
// jsonwebtoken would accept any algorithm including RS/HS confusion attacks.
|
|
194
|
-
// maxAge is a safety net even if exp is set far in the future.
|
|
195
|
-
const decoded = jwt.verify(token, JWT_SECRET, {
|
|
196
|
-
algorithms: ["HS256"],
|
|
197
|
-
maxAge: process.env.JWT_MAX_AGE || "1h",
|
|
198
|
-
}) as { sub?: unknown };
|
|
199
|
-
if (typeof decoded.sub !== "string" || decoded.sub.length === 0) {
|
|
200
|
-
(req as any).auth = { authenticated: false, actor_id: null, trace_id: traceId };
|
|
201
|
-
next();
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
(req as any).auth = {
|
|
205
|
-
authenticated: true,
|
|
206
|
-
actor_id: decoded.sub,
|
|
207
|
-
trace_id: traceId,
|
|
208
|
-
};
|
|
209
|
-
} catch {
|
|
210
|
-
(req as any).auth = { authenticated: false, actor_id: null, trace_id: traceId };
|
|
211
|
-
}
|
|
212
|
-
next();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export function requireAuth(req: Request, res: Response, next: NextFunction): void {
|
|
216
|
-
const auth: AuthContext = (req as any).auth;
|
|
217
|
-
if (!auth || !auth.authenticated) {
|
|
218
|
-
res.status(401).json({ error: { code: "UNAUTHORIZED", message: "Authentication required" } });
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
next();
|
|
222
|
-
}
|
|
143
|
+
return `// Generated by BoneScript compiler. DO NOT EDIT.
|
|
144
|
+
import { Request, Response, NextFunction } from "express";
|
|
145
|
+
import jwt from "jsonwebtoken";
|
|
146
|
+
import { v4 as uuid } from "uuid";
|
|
147
|
+
|
|
148
|
+
// JWT_SECRET must be set in production. The server will refuse to start without it
|
|
149
|
+
// when NODE_ENV is "production" to prevent accidental use of a weak fallback.
|
|
150
|
+
const JWT_SECRET = (() => {
|
|
151
|
+
const secret = process.env.JWT_SECRET;
|
|
152
|
+
if (!secret) {
|
|
153
|
+
if (process.env.NODE_ENV === "production") {
|
|
154
|
+
console.error("[FATAL] JWT_SECRET environment variable is not set. Refusing to start in production.");
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
console.warn("[WARN] JWT_SECRET is not set. Using insecure default — do not use in production.");
|
|
158
|
+
return "bonescript-dev-secret-do-not-use-in-production";
|
|
159
|
+
}
|
|
160
|
+
if (secret.length < 32) {
|
|
161
|
+
console.warn("[WARN] JWT_SECRET is shorter than 32 characters. Use a longer secret in production.");
|
|
162
|
+
}
|
|
163
|
+
return secret;
|
|
164
|
+
})();
|
|
165
|
+
|
|
166
|
+
export interface AuthContext {
|
|
167
|
+
authenticated: boolean;
|
|
168
|
+
actor_id: string | null;
|
|
169
|
+
trace_id: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Trace IDs are server-generated. We accept a client-supplied X-Trace-Id only
|
|
173
|
+
// if it parses as a UUID, so callers can correlate their own logs without
|
|
174
|
+
// being able to forge correlation IDs in audit / event records.
|
|
175
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
176
|
+
function resolveTraceId(req: Request): string {
|
|
177
|
+
const supplied = req.headers["x-trace-id"];
|
|
178
|
+
if (typeof supplied === "string" && UUID_RE.test(supplied)) return supplied;
|
|
179
|
+
return uuid();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
183
|
+
const header = req.headers.authorization;
|
|
184
|
+
const traceId = resolveTraceId(req);
|
|
185
|
+
if (!header || !header.startsWith("Bearer ")) {
|
|
186
|
+
(req as any).auth = { authenticated: false, actor_id: null, trace_id: traceId };
|
|
187
|
+
next();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const token = header.slice(7);
|
|
192
|
+
// Pin algorithms to HS256 and require an expiration. Without pinning,
|
|
193
|
+
// jsonwebtoken would accept any algorithm including RS/HS confusion attacks.
|
|
194
|
+
// maxAge is a safety net even if exp is set far in the future.
|
|
195
|
+
const decoded = jwt.verify(token, JWT_SECRET, {
|
|
196
|
+
algorithms: ["HS256"],
|
|
197
|
+
maxAge: process.env.JWT_MAX_AGE || "1h",
|
|
198
|
+
}) as { sub?: unknown };
|
|
199
|
+
if (typeof decoded.sub !== "string" || decoded.sub.length === 0) {
|
|
200
|
+
(req as any).auth = { authenticated: false, actor_id: null, trace_id: traceId };
|
|
201
|
+
next();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
(req as any).auth = {
|
|
205
|
+
authenticated: true,
|
|
206
|
+
actor_id: decoded.sub,
|
|
207
|
+
trace_id: traceId,
|
|
208
|
+
};
|
|
209
|
+
} catch {
|
|
210
|
+
(req as any).auth = { authenticated: false, actor_id: null, trace_id: traceId };
|
|
211
|
+
}
|
|
212
|
+
next();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function requireAuth(req: Request, res: Response, next: NextFunction): void {
|
|
216
|
+
const auth: AuthContext = (req as any).auth;
|
|
217
|
+
if (!auth || !auth.authenticated) {
|
|
218
|
+
res.status(401).json({ error: { code: "UNAUTHORIZED", message: "Authentication required" } });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
next();
|
|
222
|
+
}
|
|
223
223
|
`;
|
|
224
224
|
}
|
|
225
225
|
exports.emitAuthMiddleware = emitAuthMiddleware;
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -75,7 +75,7 @@ function main() {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function showHelp() {
|
|
78
|
-
console.log("BoneScript compiler v0.6.
|
|
78
|
+
console.log("BoneScript compiler v0.6.1");
|
|
79
79
|
console.log("");
|
|
80
80
|
console.log("Usage:");
|
|
81
81
|
console.log(" bonec compile <file> [--target <target>] Compile to runnable project");
|