@webstir-io/webstir-backend 0.1.15

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.
Files changed (79) hide show
  1. package/README.md +427 -0
  2. package/dist/build/artifacts.d.ts +113 -0
  3. package/dist/build/artifacts.js +53 -0
  4. package/dist/build/entries.d.ts +1 -0
  5. package/dist/build/entries.js +17 -0
  6. package/dist/build/pipeline.d.ts +31 -0
  7. package/dist/build/pipeline.js +424 -0
  8. package/dist/cache/diff.d.ts +4 -0
  9. package/dist/cache/diff.js +114 -0
  10. package/dist/cache/reporters.d.ts +12 -0
  11. package/dist/cache/reporters.js +23 -0
  12. package/dist/diagnostics/summary.d.ts +6 -0
  13. package/dist/diagnostics/summary.js +27 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +2 -0
  16. package/dist/manifest/pipeline.d.ts +13 -0
  17. package/dist/manifest/pipeline.js +224 -0
  18. package/dist/provider.d.ts +2 -0
  19. package/dist/provider.js +101 -0
  20. package/dist/scaffold/assets.d.ts +2 -0
  21. package/dist/scaffold/assets.js +77 -0
  22. package/dist/testing/context.d.ts +3 -0
  23. package/dist/testing/context.js +14 -0
  24. package/dist/testing/index.d.ts +6 -0
  25. package/dist/testing/index.js +208 -0
  26. package/dist/testing/types.d.ts +28 -0
  27. package/dist/testing/types.js +1 -0
  28. package/dist/watch.d.ts +8 -0
  29. package/dist/watch.js +159 -0
  30. package/dist/workspace.d.ts +4 -0
  31. package/dist/workspace.js +15 -0
  32. package/package.json +74 -0
  33. package/scripts/publish.sh +99 -0
  34. package/scripts/smoke.mjs +241 -0
  35. package/scripts/update-contract.sh +122 -0
  36. package/src/build/artifacts.ts +67 -0
  37. package/src/build/entries.ts +19 -0
  38. package/src/build/pipeline.ts +507 -0
  39. package/src/cache/diff.ts +128 -0
  40. package/src/cache/reporters.ts +41 -0
  41. package/src/diagnostics/summary.ts +32 -0
  42. package/src/index.ts +2 -0
  43. package/src/manifest/pipeline.ts +270 -0
  44. package/src/provider.ts +124 -0
  45. package/src/scaffold/assets.ts +81 -0
  46. package/src/testing/context.d.ts +3 -0
  47. package/src/testing/context.js +14 -0
  48. package/src/testing/context.ts +17 -0
  49. package/src/testing/index.d.ts +6 -0
  50. package/src/testing/index.js +208 -0
  51. package/src/testing/index.ts +252 -0
  52. package/src/testing/types.d.ts +28 -0
  53. package/src/testing/types.js +1 -0
  54. package/src/testing/types.ts +32 -0
  55. package/src/watch.ts +177 -0
  56. package/src/workspace.ts +22 -0
  57. package/templates/backend/.env.example +13 -0
  58. package/templates/backend/auth/adapter.ts +160 -0
  59. package/templates/backend/db/connection.ts +99 -0
  60. package/templates/backend/db/migrate.ts +231 -0
  61. package/templates/backend/db/migrations/0001-example.ts +17 -0
  62. package/templates/backend/db/types.d.ts +2 -0
  63. package/templates/backend/env.ts +174 -0
  64. package/templates/backend/functions/hello/index.ts +29 -0
  65. package/templates/backend/index.ts +532 -0
  66. package/templates/backend/jobs/nightly/index.ts +28 -0
  67. package/templates/backend/jobs/runtime.ts +103 -0
  68. package/templates/backend/jobs/scheduler.ts +193 -0
  69. package/templates/backend/module.ts +87 -0
  70. package/templates/backend/observability/logger.ts +24 -0
  71. package/templates/backend/observability/metrics.ts +78 -0
  72. package/templates/backend/server/fastify.ts +288 -0
  73. package/templates/backend/tsconfig.json +19 -0
  74. package/tests/cacheReporter.test.js +89 -0
  75. package/tests/envLoader.test.js +64 -0
  76. package/tests/integration.test.js +108 -0
  77. package/tests/manifest.test.js +159 -0
  78. package/tests/watch.test.js +100 -0
  79. package/tsconfig.json +27 -0
@@ -0,0 +1,174 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
6
+
7
+ export interface AuthSecrets {
8
+ jwtSecret?: string;
9
+ jwtIssuer?: string;
10
+ jwtAudience?: string;
11
+ serviceTokens: string[];
12
+ }
13
+
14
+ export interface LoggingConfig {
15
+ level: LogLevel;
16
+ serviceName: string;
17
+ }
18
+
19
+ export interface MetricsConfig {
20
+ enabled: boolean;
21
+ windowSize: number;
22
+ }
23
+
24
+ export interface DatabaseConfig {
25
+ url: string;
26
+ migrationsTable: string;
27
+ }
28
+
29
+ export interface AppEnv {
30
+ NODE_ENV: string;
31
+ PORT: number;
32
+ API_BASE_URL: string;
33
+ auth: AuthSecrets;
34
+ logging: LoggingConfig;
35
+ metrics: MetricsConfig;
36
+ database: DatabaseConfig;
37
+ }
38
+
39
+ const ENV_FILES = ['.env.local', '.env'];
40
+ const WORKSPACE_ROOT = resolveWorkspaceRoot();
41
+ let envLoaded = false;
42
+
43
+ export function loadEnv(): AppEnv {
44
+ if (!envLoaded) {
45
+ loadEnvFiles();
46
+ envLoaded = true;
47
+ }
48
+
49
+ const NODE_ENV = process.env.NODE_ENV ?? 'development';
50
+ const PORT = parsePort(process.env.PORT ?? '4000');
51
+ const API_BASE_URL = requireEnv('API_BASE_URL', 'http://localhost:4000');
52
+ const auth: AuthSecrets = {
53
+ jwtSecret: process.env.AUTH_JWT_SECRET,
54
+ jwtIssuer: process.env.AUTH_JWT_ISSUER,
55
+ jwtAudience: process.env.AUTH_JWT_AUDIENCE,
56
+ serviceTokens: parseList(process.env.AUTH_SERVICE_TOKENS)
57
+ };
58
+ const logging: LoggingConfig = {
59
+ level: parseLogLevel(process.env.LOG_LEVEL),
60
+ serviceName: process.env.LOG_SERVICE_NAME ?? 'backend-template'
61
+ };
62
+ const metrics: MetricsConfig = {
63
+ enabled: parseBoolean(process.env.METRICS_ENABLED, true),
64
+ windowSize: parsePositiveInt(process.env.METRICS_WINDOW, 200)
65
+ };
66
+ const database: DatabaseConfig = {
67
+ url: process.env.DATABASE_URL ?? 'file:./data/dev.sqlite',
68
+ migrationsTable: process.env.DATABASE_MIGRATIONS_TABLE ?? '_webstir_migrations'
69
+ };
70
+
71
+ return {
72
+ NODE_ENV,
73
+ PORT,
74
+ API_BASE_URL,
75
+ auth,
76
+ logging,
77
+ metrics,
78
+ database
79
+ };
80
+ }
81
+
82
+ function loadEnvFiles(): void {
83
+ for (const file of ENV_FILES) {
84
+ const full = path.resolve(WORKSPACE_ROOT, file);
85
+ if (!existsSync(full)) continue;
86
+ try {
87
+ applyEnvFile(full);
88
+ } catch (error) {
89
+ console.warn(`[webstir-backend] failed to load ${file}: ${(error as Error).message}`);
90
+ }
91
+ }
92
+ }
93
+
94
+ function applyEnvFile(filePath: string): void {
95
+ const lines = readFileSync(filePath, 'utf8').split(/\r?\n/);
96
+ for (const line of lines) {
97
+ if (!line || line.trim().startsWith('#')) continue;
98
+ const idx = line.indexOf('=');
99
+ if (idx === -1) continue;
100
+ const key = line.slice(0, idx).trim();
101
+ if (!key || process.env[key] !== undefined) continue;
102
+ let value = line.slice(idx + 1).trim();
103
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\''))) {
104
+ value = value.slice(1, -1);
105
+ }
106
+ process.env[key] = value;
107
+ }
108
+ }
109
+
110
+ function requireEnv(name: string, fallback?: string): string {
111
+ const value = process.env[name] ?? fallback;
112
+ if (value === undefined) {
113
+ throw new Error(`Missing required env var ${name}`);
114
+ }
115
+ return value;
116
+ }
117
+
118
+ function parsePort(value: string): number {
119
+ const parsed = Number(value);
120
+ if (!Number.isFinite(parsed) || parsed <= 0) {
121
+ return 4000;
122
+ }
123
+ return parsed;
124
+ }
125
+
126
+ function parseList(value: string | undefined): string[] {
127
+ if (!value) return [];
128
+ return value
129
+ .split(/[,\s]+/)
130
+ .map((item) => item.trim())
131
+ .filter((item) => item.length > 0);
132
+ }
133
+
134
+ function parseLogLevel(value: string | undefined): LogLevel {
135
+ const normalized = (value ?? 'info').toLowerCase();
136
+ if (normalized === 'trace' || normalized === 'debug' || normalized === 'info' || normalized === 'warn' || normalized === 'error' || normalized === 'fatal') {
137
+ return normalized;
138
+ }
139
+ return 'info';
140
+ }
141
+
142
+ function parseBoolean(value: string | undefined, fallback: boolean): boolean {
143
+ if (value === undefined) return fallback;
144
+ const normalized = value.toLowerCase();
145
+ return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
146
+ }
147
+
148
+ function parsePositiveInt(value: string | undefined, fallback: number): number {
149
+ if (!value) return fallback;
150
+ const parsed = Number(value);
151
+ if (!Number.isFinite(parsed) || parsed <= 0) {
152
+ return fallback;
153
+ }
154
+ return Math.floor(parsed);
155
+ }
156
+
157
+ export function resolveWorkspaceRoot(): string {
158
+ if (process.env.WORKSPACE_ROOT) {
159
+ return process.env.WORKSPACE_ROOT;
160
+ }
161
+ try {
162
+ const filePath = fileURLToPath(import.meta.url);
163
+ const dir = path.dirname(filePath);
164
+ if (dir.endsWith(`${path.sep}src${path.sep}backend`)) {
165
+ return path.resolve(dir, '..', '..');
166
+ }
167
+ if (dir.endsWith(`${path.sep}build${path.sep}backend`)) {
168
+ return path.resolve(dir, '..', '..');
169
+ }
170
+ } catch {
171
+ // ignore
172
+ }
173
+ return process.cwd();
174
+ }
@@ -0,0 +1,29 @@
1
+ // Example function entry (invoked by your runtime)
2
+ // This is a simple placeholder; wire it into your job/queue system as needed.
3
+
4
+ export async function run(): Promise<void> {
5
+ // Do some background work here
6
+ // e.g., send an email, process a small batch, etc.
7
+ console.info('[function:hello] ran at', new Date().toISOString());
8
+ }
9
+
10
+ // Execute when launched directly: `node build/backend/functions/hello/index.js`
11
+ const isMain = (() => {
12
+ try {
13
+ const argv1 = process.argv?.[1];
14
+ if (!argv1) return false;
15
+ const here = new URL(import.meta.url);
16
+ const run = new URL(`file://${argv1}`);
17
+ return here.pathname === run.pathname;
18
+ } catch {
19
+ return false;
20
+ }
21
+ })();
22
+
23
+ if (isMain) {
24
+ run().catch((err) => {
25
+ console.error(err);
26
+ process.exitCode = 1;
27
+ });
28
+ }
29
+