@objectifthunes/create-sandstone 0.1.0 → 1.0.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.
Files changed (91) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +31 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/add-adapter.d.ts +2 -0
  6. package/dist/commands/add-adapter.d.ts.map +1 -0
  7. package/dist/commands/add-adapter.js +683 -0
  8. package/dist/commands/add-adapter.js.map +1 -0
  9. package/dist/commands/generate-adapter.d.ts +2 -0
  10. package/dist/commands/generate-adapter.d.ts.map +1 -0
  11. package/dist/commands/generate-adapter.js +467 -0
  12. package/dist/commands/generate-adapter.js.map +1 -0
  13. package/dist/commands/generate-entity.d.ts +2 -0
  14. package/dist/commands/generate-entity.d.ts.map +1 -0
  15. package/dist/commands/generate-entity.js +210 -0
  16. package/dist/commands/generate-entity.js.map +1 -0
  17. package/dist/generator.d.ts +3 -0
  18. package/dist/generator.d.ts.map +1 -0
  19. package/dist/generator.js +85 -0
  20. package/dist/generator.js.map +1 -0
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +107 -61
  24. package/dist/index.js.map +1 -0
  25. package/dist/presets.d.ts +45 -0
  26. package/dist/presets.d.ts.map +1 -0
  27. package/dist/presets.js +105 -0
  28. package/dist/presets.js.map +1 -0
  29. package/dist/prompts.d.ts +3 -13
  30. package/dist/prompts.d.ts.map +1 -0
  31. package/dist/prompts.js +222 -49
  32. package/dist/prompts.js.map +1 -0
  33. package/dist/templates/claude-md.d.ts +3 -0
  34. package/dist/templates/claude-md.d.ts.map +1 -0
  35. package/dist/templates/claude-md.js +853 -0
  36. package/dist/templates/claude-md.js.map +1 -0
  37. package/dist/templates/docker-compose.d.ts +3 -0
  38. package/dist/templates/docker-compose.d.ts.map +1 -0
  39. package/dist/templates/docker-compose.js +75 -0
  40. package/dist/templates/docker-compose.js.map +1 -0
  41. package/dist/templates/env-example.d.ts +3 -0
  42. package/dist/templates/env-example.d.ts.map +1 -0
  43. package/dist/templates/env-example.js +137 -0
  44. package/dist/templates/env-example.js.map +1 -0
  45. package/dist/templates/gitignore.d.ts +2 -0
  46. package/dist/templates/gitignore.d.ts.map +1 -0
  47. package/dist/templates/gitignore.js +11 -0
  48. package/dist/templates/gitignore.js.map +1 -0
  49. package/dist/templates/infrastructure.d.ts +3 -0
  50. package/dist/templates/infrastructure.d.ts.map +1 -0
  51. package/dist/templates/infrastructure.js +647 -0
  52. package/dist/templates/infrastructure.js.map +1 -0
  53. package/dist/templates/main-express.d.ts +3 -0
  54. package/dist/templates/main-express.d.ts.map +1 -0
  55. package/dist/templates/main-express.js +80 -0
  56. package/dist/templates/main-express.js.map +1 -0
  57. package/dist/templates/main-fastify.d.ts +3 -0
  58. package/dist/templates/main-fastify.d.ts.map +1 -0
  59. package/dist/templates/main-fastify.js +73 -0
  60. package/dist/templates/main-fastify.js.map +1 -0
  61. package/dist/templates/main-hono.d.ts +3 -0
  62. package/dist/templates/main-hono.d.ts.map +1 -0
  63. package/dist/templates/main-hono.js +83 -0
  64. package/dist/templates/main-hono.js.map +1 -0
  65. package/dist/templates/migrate-script.d.ts +2 -0
  66. package/dist/templates/migrate-script.d.ts.map +1 -0
  67. package/dist/templates/migrate-script.js +18 -0
  68. package/dist/templates/migrate-script.js.map +1 -0
  69. package/dist/templates/migration.d.ts +2 -0
  70. package/dist/templates/migration.d.ts.map +1 -0
  71. package/dist/templates/migration.js +17 -0
  72. package/dist/templates/migration.js.map +1 -0
  73. package/dist/templates/package-json.d.ts +3 -0
  74. package/dist/templates/package-json.d.ts.map +1 -0
  75. package/dist/templates/package-json.js +149 -0
  76. package/dist/templates/package-json.js.map +1 -0
  77. package/dist/templates/schema.d.ts +3 -0
  78. package/dist/templates/schema.d.ts.map +1 -0
  79. package/dist/templates/schema.js +108 -0
  80. package/dist/templates/schema.js.map +1 -0
  81. package/dist/templates/test-setup.d.ts +3 -0
  82. package/dist/templates/test-setup.d.ts.map +1 -0
  83. package/dist/templates/test-setup.js +12 -0
  84. package/dist/templates/test-setup.js.map +1 -0
  85. package/dist/templates/tsconfig.d.ts +2 -0
  86. package/dist/templates/tsconfig.d.ts.map +1 -0
  87. package/dist/templates/tsconfig.js +21 -0
  88. package/dist/templates/tsconfig.js.map +1 -0
  89. package/package.json +16 -9
  90. package/dist/generators.d.ts +0 -7
  91. package/dist/generators.js +0 -328
@@ -0,0 +1,647 @@
1
+ export function infrastructureTemplate(config) {
2
+ const imports = [];
3
+ const inits = [];
4
+ const appOptions = [];
5
+ const devMode = config.devMode === true;
6
+ const devPath = `'@objectifthunes/sandstone-sdk/dev'`;
7
+ const isSocketIo = config.realtime === 'socketio';
8
+ // Core imports always needed
9
+ imports.push(`import { createApp } from '@objectifthunes/sandstone-sdk'`);
10
+ // ── Database ──────────────────────────────────────────────────────────────
11
+ if (devMode) {
12
+ if (config.database === 'supabase') {
13
+ imports.push(`import { createSupabaseClient } from ${devPath}`);
14
+ inits.push(`const db = createSupabaseClient()`);
15
+ }
16
+ else {
17
+ imports.push(`import { createPgClient } from ${devPath}`);
18
+ inits.push(`const db = createPgClient()`);
19
+ }
20
+ }
21
+ else if (config.database === 'supabase') {
22
+ imports.push(`import { createSupabaseClient } from '@objectifthunes/sandstone-sdk/supabase'`);
23
+ inits.push(`const db = createSupabaseClient({ connectionString: process.env.DATABASE_URL! })`);
24
+ }
25
+ else {
26
+ imports.push(`import { createPgClient } from '@objectifthunes/sandstone-sdk/pg'`);
27
+ inits.push(`const db = createPgClient({ connectionString: process.env.DATABASE_URL! })`);
28
+ }
29
+ appOptions.push('db');
30
+ // ── Logger ────────────────────────────────────────────────────────────────
31
+ if (devMode) {
32
+ imports.push(`import { createPinoLogger } from ${devPath}`);
33
+ inits.push(`const logger = createPinoLogger()`);
34
+ }
35
+ else {
36
+ imports.push(`import { createPinoLogger } from '@objectifthunes/sandstone-sdk/pino'`);
37
+ inits.push(`const logger = createPinoLogger({ level: process.env.LOG_LEVEL ?? 'info' })`);
38
+ }
39
+ appOptions.push('logger');
40
+ // ── Tokens ────────────────────────────────────────────────────────────────
41
+ if (devMode) {
42
+ imports.push(`import { createJoseSigner } from ${devPath}`);
43
+ inits.push(`const tokens = createJoseSigner()`);
44
+ }
45
+ else {
46
+ imports.push(`import { createJoseSigner } from '@objectifthunes/sandstone-sdk/jose'`);
47
+ inits.push(`const tokens = createJoseSigner({ secret: process.env.JWT_SECRET! })`);
48
+ }
49
+ appOptions.push('tokens');
50
+ // ── Tracing ───────────────────────────────────────────────────────────────
51
+ if (config.tracing) {
52
+ if (devMode) {
53
+ imports.push(`import { createOtelTracer } from ${devPath}`);
54
+ inits.push(`const tracer = createOtelTracer()`);
55
+ }
56
+ else {
57
+ imports.push(`import { createOtelTracer } from '@objectifthunes/sandstone-sdk/otel'`);
58
+ inits.push(`const tracer = createOtelTracer({ serviceName: process.env.OTEL_SERVICE_NAME ?? 'app' })`);
59
+ }
60
+ appOptions.push('tracer');
61
+ }
62
+ // ── Email ─────────────────────────────────────────────────────────────────
63
+ if (devMode && config.email) {
64
+ imports.push(`import { createNodemailerTransport } from ${devPath}`);
65
+ inits.push(`const emailTransport = createNodemailerTransport()`);
66
+ appOptions.push(`email: { transport: emailTransport, from: 'dev@localhost' }`);
67
+ }
68
+ else if (config.email === 'resend') {
69
+ imports.push(`import { createResendTransport } from '@objectifthunes/sandstone-sdk/resend'`);
70
+ inits.push(`const emailTransport = createResendTransport({ apiKey: process.env.RESEND_API_KEY! })`);
71
+ appOptions.push(`email: { transport: emailTransport, from: process.env.EMAIL_FROM ?? 'noreply@example.com' }`);
72
+ }
73
+ else if (config.email === 'nodemailer') {
74
+ imports.push(`import { createNodemailerTransport } from '@objectifthunes/sandstone-sdk/nodemailer'`);
75
+ inits.push(`const emailTransport = createNodemailerTransport({
76
+ host: process.env.SMTP_HOST ?? 'localhost',
77
+ port: Number(process.env.SMTP_PORT ?? 1025),
78
+ auth: process.env.SMTP_USER ? { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS ?? '' } : undefined,
79
+ })`);
80
+ appOptions.push(`email: { transport: emailTransport, from: process.env.EMAIL_FROM ?? 'noreply@example.com' }`);
81
+ }
82
+ // ── Auth ──────────────────────────────────────────────────────────────────
83
+ if (config.auth) {
84
+ const authOpts = [`otp: { length: 6, expiresIn: '10m' }`];
85
+ appOptions.push(`auth: { ${authOpts.join(', ')} }`);
86
+ }
87
+ // ── Password hasher (for password+otp or all) ────────────────────────────
88
+ if (config.auth === 'password+otp' || config.auth === 'all') {
89
+ if (devMode) {
90
+ imports.push(`import { createArgon2Hasher } from ${devPath}`);
91
+ }
92
+ else {
93
+ imports.push(`import { createArgon2Hasher } from '@objectifthunes/sandstone-sdk/argon2'`);
94
+ }
95
+ inits.push(`const passwordHasher = createArgon2Hasher()`);
96
+ }
97
+ // ── OAuth providers (for oauth+otp or all) ───────────────────────────────
98
+ if ((config.auth === 'oauth+otp' || config.auth === 'all') && config.oauthProviders.length > 0) {
99
+ const providerMap = {
100
+ google: {
101
+ factory: 'createGoogleOAuth',
102
+ subpath: 'oauth-google',
103
+ envVars: `{
104
+ clientId: process.env.GOOGLE_CLIENT_ID!,
105
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
106
+ redirectUri: process.env.GOOGLE_REDIRECT_URI!,
107
+ }`,
108
+ },
109
+ github: {
110
+ factory: 'createGitHubOAuth',
111
+ subpath: 'oauth-github',
112
+ envVars: `{
113
+ clientId: process.env.GITHUB_CLIENT_ID!,
114
+ clientSecret: process.env.GITHUB_CLIENT_SECRET!,
115
+ redirectUri: process.env.GITHUB_REDIRECT_URI!,
116
+ }`,
117
+ },
118
+ apple: {
119
+ factory: 'createAppleOAuth',
120
+ subpath: 'oauth-apple',
121
+ envVars: `{
122
+ clientId: process.env.APPLE_CLIENT_ID!,
123
+ clientSecret: process.env.APPLE_CLIENT_SECRET!,
124
+ teamId: process.env.APPLE_TEAM_ID!,
125
+ keyId: process.env.APPLE_KEY_ID!,
126
+ privateKey: process.env.APPLE_PRIVATE_KEY!,
127
+ }`,
128
+ },
129
+ discord: {
130
+ factory: 'createDiscordOAuth',
131
+ subpath: 'oauth-discord',
132
+ envVars: `{
133
+ clientId: process.env.DISCORD_CLIENT_ID!,
134
+ clientSecret: process.env.DISCORD_CLIENT_SECRET!,
135
+ redirectUri: process.env.DISCORD_REDIRECT_URI!,
136
+ }`,
137
+ },
138
+ };
139
+ for (const provider of config.oauthProviders) {
140
+ const info = providerMap[provider];
141
+ if (!info)
142
+ continue;
143
+ const varName = `${provider}OAuth`;
144
+ if (devMode) {
145
+ imports.push(`import { ${info.factory} } from ${devPath}`);
146
+ inits.push(`const ${varName} = ${info.factory}()`);
147
+ }
148
+ else {
149
+ imports.push(`import { ${info.factory} } from '@objectifthunes/sandstone-sdk/${info.subpath}'`);
150
+ inits.push(`const ${varName} = ${info.factory}(${info.envVars})`);
151
+ }
152
+ }
153
+ }
154
+ // ── SMS ───────────────────────────────────────────────────────────────────
155
+ if (config.sms === 'twilio') {
156
+ if (devMode) {
157
+ imports.push(`import { createTwilioSms } from ${devPath}`);
158
+ inits.push(`const sms = createTwilioSms()`);
159
+ }
160
+ else {
161
+ imports.push(`import { createTwilioSms } from '@objectifthunes/sandstone-sdk/twilio'`);
162
+ inits.push(`const sms = createTwilioSms({
163
+ accountSid: process.env.TWILIO_ACCOUNT_SID!,
164
+ authToken: process.env.TWILIO_AUTH_TOKEN!,
165
+ from: process.env.TWILIO_FROM_NUMBER!,
166
+ })`);
167
+ }
168
+ appOptions.push('sms');
169
+ }
170
+ else if (config.sms === 'sns') {
171
+ if (devMode) {
172
+ imports.push(`import { createSnsSms } from ${devPath}`);
173
+ inits.push(`const sms = createSnsSms()`);
174
+ }
175
+ else {
176
+ imports.push(`import { createSnsSms } from '@objectifthunes/sandstone-sdk/sns'`);
177
+ inits.push(`const sms = createSnsSms({
178
+ region: process.env.AWS_REGION!,
179
+ })`);
180
+ }
181
+ appOptions.push('sms');
182
+ }
183
+ // ── Push ──────────────────────────────────────────────────────────────────
184
+ if (config.push === 'fcm') {
185
+ if (devMode) {
186
+ imports.push(`import { createFcmPush } from ${devPath}`);
187
+ inits.push(`const push = createFcmPush()`);
188
+ }
189
+ else {
190
+ imports.push(`import { createFcmPush } from '@objectifthunes/sandstone-sdk/fcm'`);
191
+ inits.push(`const push = createFcmPush({
192
+ projectId: process.env.FCM_PROJECT_ID!,
193
+ clientEmail: process.env.FCM_CLIENT_EMAIL!,
194
+ privateKey: process.env.FCM_PRIVATE_KEY!,
195
+ })`);
196
+ }
197
+ appOptions.push('push');
198
+ }
199
+ else if (config.push === 'apns') {
200
+ if (devMode) {
201
+ imports.push(`import { createApnsPush } from ${devPath}`);
202
+ inits.push(`const push = createApnsPush()`);
203
+ }
204
+ else {
205
+ imports.push(`import { createApnsPush } from '@objectifthunes/sandstone-sdk/apns'`);
206
+ inits.push(`const push = createApnsPush({
207
+ keyId: process.env.APNS_KEY_ID!,
208
+ teamId: process.env.APNS_TEAM_ID!,
209
+ privateKey: process.env.APNS_PRIVATE_KEY!,
210
+ bundleId: process.env.APNS_BUNDLE_ID!,
211
+ })`);
212
+ }
213
+ appOptions.push('push');
214
+ }
215
+ else if (config.push === 'both') {
216
+ if (devMode) {
217
+ imports.push(`import { createFcmPush, createApnsPush } from ${devPath}`);
218
+ inits.push(`const fcmPush = createFcmPush()`);
219
+ inits.push(`const apnsPush = createApnsPush()`);
220
+ }
221
+ else {
222
+ imports.push(`import { createFcmPush } from '@objectifthunes/sandstone-sdk/fcm'`);
223
+ imports.push(`import { createApnsPush } from '@objectifthunes/sandstone-sdk/apns'`);
224
+ inits.push(`const fcmPush = createFcmPush({
225
+ projectId: process.env.FCM_PROJECT_ID!,
226
+ clientEmail: process.env.FCM_CLIENT_EMAIL!,
227
+ privateKey: process.env.FCM_PRIVATE_KEY!,
228
+ })`);
229
+ inits.push(`const apnsPush = createApnsPush({
230
+ keyId: process.env.APNS_KEY_ID!,
231
+ teamId: process.env.APNS_TEAM_ID!,
232
+ privateKey: process.env.APNS_PRIVATE_KEY!,
233
+ bundleId: process.env.APNS_BUNDLE_ID!,
234
+ })`);
235
+ }
236
+ // Use FCM as primary push; APNs available for fan-out wrapper
237
+ appOptions.push('push: fcmPush');
238
+ }
239
+ // ── Realtime ──────────────────────────────────────────────────────────────
240
+ if (config.realtime === 'pusher') {
241
+ if (devMode) {
242
+ imports.push(`import { createPusherRealtime } from ${devPath}`);
243
+ inits.push(`const realtime = createPusherRealtime()`);
244
+ }
245
+ else {
246
+ imports.push(`import { createPusherRealtime } from '@objectifthunes/sandstone-sdk/pusher'`);
247
+ inits.push(`const realtime = createPusherRealtime({
248
+ appId: process.env.PUSHER_APP_ID!,
249
+ key: process.env.PUSHER_KEY!,
250
+ secret: process.env.PUSHER_SECRET!,
251
+ cluster: process.env.PUSHER_CLUSTER!,
252
+ })`);
253
+ }
254
+ appOptions.push('realtime');
255
+ }
256
+ else if (config.realtime === 'ably') {
257
+ if (devMode) {
258
+ imports.push(`import { createAblyRealtime } from ${devPath}`);
259
+ inits.push(`const realtime = createAblyRealtime()`);
260
+ }
261
+ else {
262
+ imports.push(`import { createAblyRealtime } from '@objectifthunes/sandstone-sdk/ably'`);
263
+ inits.push(`const realtime = createAblyRealtime({
264
+ apiKey: process.env.ABLY_API_KEY!,
265
+ })`);
266
+ }
267
+ appOptions.push('realtime');
268
+ }
269
+ // socketio is handled in the export pattern below — needs HTTP server from main.ts
270
+ // ── Search ────────────────────────────────────────────────────────────────
271
+ if (config.search === 'meilisearch') {
272
+ if (devMode) {
273
+ imports.push(`import { createMeilisearchSearch } from ${devPath}`);
274
+ inits.push(`const search = createMeilisearchSearch()`);
275
+ }
276
+ else {
277
+ imports.push(`import { createMeilisearchSearch } from '@objectifthunes/sandstone-sdk/meilisearch'`);
278
+ inits.push(`const search = createMeilisearchSearch({
279
+ host: process.env.MEILISEARCH_HOST!,
280
+ apiKey: process.env.MEILISEARCH_API_KEY!,
281
+ })`);
282
+ }
283
+ appOptions.push('search');
284
+ }
285
+ else if (config.search === 'typesense') {
286
+ if (devMode) {
287
+ imports.push(`import { createTypesenseSearch } from ${devPath}`);
288
+ inits.push(`const search = createTypesenseSearch()`);
289
+ }
290
+ else {
291
+ imports.push(`import { createTypesenseSearch } from '@objectifthunes/sandstone-sdk/typesense'`);
292
+ inits.push(`const search = createTypesenseSearch({
293
+ host: process.env.TYPESENSE_HOST!,
294
+ port: Number(process.env.TYPESENSE_PORT ?? 8108),
295
+ apiKey: process.env.TYPESENSE_API_KEY!,
296
+ })`);
297
+ }
298
+ appOptions.push('search');
299
+ }
300
+ else if (config.search === 'pg-search') {
301
+ if (devMode) {
302
+ imports.push(`import { createPgSearch } from ${devPath}`);
303
+ inits.push(`const search = createPgSearch()`);
304
+ }
305
+ else {
306
+ imports.push(`import { createPgSearch } from '@objectifthunes/sandstone-sdk/pg-search'`);
307
+ inits.push(`const search = createPgSearch({ db })`);
308
+ }
309
+ appOptions.push('search');
310
+ }
311
+ // ── Queue ─────────────────────────────────────────────────────────────────
312
+ if (config.queue === 'bullmq') {
313
+ if (devMode) {
314
+ imports.push(`import { createBullMQQueue } from ${devPath}`);
315
+ inits.push(`const queue = createBullMQQueue()`);
316
+ }
317
+ else {
318
+ imports.push(`import { createBullMQQueue } from '@objectifthunes/sandstone-sdk/bullmq'`);
319
+ inits.push(`const queue = createBullMQQueue({
320
+ connection: process.env.REDIS_URL!,
321
+ })`);
322
+ }
323
+ appOptions.push('queue');
324
+ }
325
+ else if (config.queue === 'pg-boss') {
326
+ if (devMode) {
327
+ imports.push(`import { createPgBossQueue } from ${devPath}`);
328
+ inits.push(`const queue = createPgBossQueue()`);
329
+ }
330
+ else {
331
+ imports.push(`import { createPgBossQueue } from '@objectifthunes/sandstone-sdk/pg-boss'`);
332
+ inits.push(`const queue = createPgBossQueue({
333
+ connectionString: process.env.DATABASE_URL!,
334
+ })`);
335
+ }
336
+ appOptions.push('queue');
337
+ }
338
+ // ── Scheduler (only when queue is set) ────────────────────────────────────
339
+ if (config.scheduler && config.queue) {
340
+ if (devMode) {
341
+ imports.push(`import { createNodeCronTickSource } from ${devPath}`);
342
+ inits.push(`const nodeCronTickSource = createNodeCronTickSource()`);
343
+ }
344
+ else {
345
+ imports.push(`import { createNodeCronTickSource } from '@objectifthunes/sandstone-sdk/node-cron'`);
346
+ inits.push(`const nodeCronTickSource = createNodeCronTickSource()`);
347
+ }
348
+ appOptions.push(`scheduler: { tickSource: nodeCronTickSource, tasks: [] }`);
349
+ }
350
+ // ── Feature Flags ─────────────────────────────────────────────────────────
351
+ if (config.flags === 'pg-flags') {
352
+ if (devMode) {
353
+ imports.push(`import { createPgFlags } from ${devPath}`);
354
+ inits.push(`const flags = createPgFlags()`);
355
+ }
356
+ else {
357
+ imports.push(`import { createPgFlags } from '@objectifthunes/sandstone-sdk/pg-flags'`);
358
+ inits.push(`const flags = createPgFlags({ db })`);
359
+ }
360
+ appOptions.push('flags');
361
+ }
362
+ else if (config.flags === 'env-flags') {
363
+ if (devMode) {
364
+ imports.push(`import { createEnvFlags } from ${devPath}`);
365
+ inits.push(`const flags = createEnvFlags()`);
366
+ }
367
+ else {
368
+ imports.push(`import { createEnvFlags } from '@objectifthunes/sandstone-sdk/env-flags'`);
369
+ inits.push(`const flags = createEnvFlags({ prefix: 'FLAG_' })`);
370
+ }
371
+ appOptions.push('flags');
372
+ }
373
+ else if (config.flags === 'launchdarkly') {
374
+ if (devMode) {
375
+ imports.push(`import { createLaunchDarklyFlags } from ${devPath}`);
376
+ inits.push(`const flags = createLaunchDarklyFlags()`);
377
+ }
378
+ else {
379
+ imports.push(`import { createLaunchDarklyFlags } from '@objectifthunes/sandstone-sdk/launchdarkly'`);
380
+ inits.push(`const flags = createLaunchDarklyFlags({
381
+ sdkKey: process.env.LAUNCHDARKLY_SDK_KEY!,
382
+ })`);
383
+ }
384
+ appOptions.push('flags');
385
+ }
386
+ // ── Audit ─────────────────────────────────────────────────────────────────
387
+ if (config.audit) {
388
+ appOptions.push('audit: true');
389
+ }
390
+ // ── Authorization ─────────────────────────────────────────────────────────
391
+ if (config.authz === 'pg-authz') {
392
+ if (devMode) {
393
+ imports.push(`import { createPgAuthz } from ${devPath}`);
394
+ inits.push(`const authz = createPgAuthz()`);
395
+ }
396
+ else {
397
+ imports.push(`import { createPgAuthz } from '@objectifthunes/sandstone-sdk/pg-authz'`);
398
+ inits.push(`const authz = createPgAuthz({ db })`);
399
+ }
400
+ appOptions.push('authz');
401
+ }
402
+ else if (config.authz === 'casbin') {
403
+ if (devMode) {
404
+ imports.push(`import { createCasbinAuthz } from ${devPath}`);
405
+ inits.push(`const authz = createCasbinAuthz()`);
406
+ }
407
+ else {
408
+ imports.push(`import { createCasbinAuthz } from '@objectifthunes/sandstone-sdk/casbin'`);
409
+ inits.push(`const authz = createCasbinAuthz({
410
+ modelPath: './casbin/model.conf',
411
+ policyPath: './casbin/policy.csv',
412
+ })`);
413
+ }
414
+ appOptions.push('authz');
415
+ }
416
+ // ── Payments ──────────────────────────────────────────────────────────────
417
+ if (config.payments === 'stripe') {
418
+ if (devMode) {
419
+ imports.push(`import { createStripeProvider } from ${devPath}`);
420
+ inits.push(`const paymentProvider = createStripeProvider()`);
421
+ }
422
+ else {
423
+ imports.push(`import { createStripeProvider } from '@objectifthunes/sandstone-sdk/stripe'`);
424
+ inits.push(`const paymentProvider = createStripeProvider({
425
+ secretKey: process.env.STRIPE_SECRET_KEY!,
426
+ webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
427
+ })`);
428
+ }
429
+ appOptions.push('payments: { provider: paymentProvider }');
430
+ }
431
+ else if (config.payments === 'revenuecat') {
432
+ if (devMode) {
433
+ imports.push(`import { createRevenueCatProvider } from ${devPath}`);
434
+ inits.push(`const paymentProvider = createRevenueCatProvider()`);
435
+ }
436
+ else {
437
+ imports.push(`import { createRevenueCatProvider } from '@objectifthunes/sandstone-sdk/revenuecat'`);
438
+ inits.push(`const paymentProvider = createRevenueCatProvider({
439
+ apiKey: process.env.REVENUECAT_API_KEY!,
440
+ webhookSecret: process.env.REVENUECAT_WEBHOOK_SECRET!,
441
+ })`);
442
+ }
443
+ appOptions.push('payments: { provider: paymentProvider }');
444
+ }
445
+ else if (config.payments === 'paddle') {
446
+ if (devMode) {
447
+ imports.push(`import { createPaddleProvider } from ${devPath}`);
448
+ inits.push(`const paymentProvider = createPaddleProvider()`);
449
+ }
450
+ else {
451
+ imports.push(`import { createPaddleProvider } from '@objectifthunes/sandstone-sdk/paddle'`);
452
+ inits.push(`const paymentProvider = createPaddleProvider({
453
+ apiKey: process.env.PADDLE_API_KEY!,
454
+ webhookSecret: process.env.PADDLE_WEBHOOK_SECRET!,
455
+ environment: (process.env.PADDLE_ENVIRONMENT as 'sandbox' | 'production') ?? 'sandbox',
456
+ })`);
457
+ }
458
+ appOptions.push('payments: { provider: paymentProvider }');
459
+ }
460
+ else if (config.payments === 'lemonsqueezy') {
461
+ if (devMode) {
462
+ imports.push(`import { createLemonSqueezyProvider } from ${devPath}`);
463
+ inits.push(`const paymentProvider = createLemonSqueezyProvider()`);
464
+ }
465
+ else {
466
+ imports.push(`import { createLemonSqueezyProvider } from '@objectifthunes/sandstone-sdk/lemonsqueezy'`);
467
+ inits.push(`const paymentProvider = createLemonSqueezyProvider({
468
+ apiKey: process.env.LEMONSQUEEZY_API_KEY!,
469
+ webhookSecret: process.env.LEMONSQUEEZY_WEBHOOK_SECRET!,
470
+ })`);
471
+ }
472
+ appOptions.push('payments: { provider: paymentProvider }');
473
+ }
474
+ // ── Storage ───────────────────────────────────────────────────────────────
475
+ if (config.storage === 's3') {
476
+ if (devMode) {
477
+ imports.push(`import { createS3Storage } from ${devPath}`);
478
+ inits.push(`const storage = createS3Storage()`);
479
+ }
480
+ else {
481
+ imports.push(`import { createS3Storage } from '@objectifthunes/sandstone-sdk/s3'`);
482
+ inits.push(`const storage = createS3Storage({
483
+ bucket: process.env.S3_BUCKET!,
484
+ region: process.env.S3_REGION!,
485
+ credentials: {
486
+ accessKeyId: process.env.S3_ACCESS_KEY_ID!,
487
+ secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
488
+ },
489
+ })`);
490
+ }
491
+ }
492
+ else if (config.storage === 'r2') {
493
+ if (devMode) {
494
+ imports.push(`import { createR2Storage } from ${devPath}`);
495
+ inits.push(`const storage = createR2Storage()`);
496
+ }
497
+ else {
498
+ imports.push(`import { createR2Storage } from '@objectifthunes/sandstone-sdk/r2'`);
499
+ inits.push(`const storage = createR2Storage({
500
+ accountId: process.env.R2_ACCOUNT_ID!,
501
+ accessKeyId: process.env.R2_ACCESS_KEY_ID!,
502
+ secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
503
+ bucket: process.env.R2_BUCKET!,
504
+ })`);
505
+ }
506
+ }
507
+ else if (config.storage === 'local') {
508
+ if (devMode) {
509
+ imports.push(`import { createLocalStorage } from ${devPath}`);
510
+ inits.push(`const storage = createLocalStorage()`);
511
+ }
512
+ else {
513
+ imports.push(`import { createLocalStorage } from '@objectifthunes/sandstone-sdk/local-storage'`);
514
+ inits.push(`const storage = createLocalStorage({
515
+ directory: process.env.LOCAL_STORAGE_DIR ?? './uploads',
516
+ })`);
517
+ }
518
+ }
519
+ // ── Cache ─────────────────────────────────────────────────────────────────
520
+ if (config.cache === 'upstash') {
521
+ if (devMode) {
522
+ imports.push(`import { createUpstashCache } from ${devPath}`);
523
+ inits.push(`const cache = createUpstashCache()`);
524
+ }
525
+ else {
526
+ imports.push(`import { createUpstashCache } from '@objectifthunes/sandstone-sdk/upstash'`);
527
+ inits.push(`const cache = createUpstashCache({
528
+ url: process.env.UPSTASH_REDIS_URL!,
529
+ token: process.env.UPSTASH_REDIS_TOKEN!,
530
+ })`);
531
+ }
532
+ }
533
+ else if (config.cache === 'redis') {
534
+ if (devMode) {
535
+ imports.push(`import { createRedisCache } from ${devPath}`);
536
+ inits.push(`const cache = createRedisCache()`);
537
+ }
538
+ else {
539
+ imports.push(`import { createRedisCache } from '@objectifthunes/sandstone-sdk/redis'`);
540
+ inits.push(`const cache = createRedisCache({
541
+ url: process.env.REDIS_URL!,
542
+ })`);
543
+ }
544
+ }
545
+ else if (config.cache === 'memory') {
546
+ if (devMode) {
547
+ imports.push(`import { createMemoryCache } from ${devPath}`);
548
+ inits.push(`const cache = createMemoryCache()`);
549
+ }
550
+ else {
551
+ imports.push(`import { createMemoryCache } from '@objectifthunes/sandstone-sdk/memory'`);
552
+ inits.push(`const cache = createMemoryCache()`);
553
+ }
554
+ }
555
+ // ── GraphQL ───────────────────────────────────────────────────────────────
556
+ if (config.graphql) {
557
+ imports.push(`import { schema } from './schema.js'`);
558
+ appOptions.push(`graphql: { schema }`);
559
+ }
560
+ // ── Dashboard ─────────────────────────────────────────────────────────────
561
+ if (config.dashboard) {
562
+ imports.push(`import { createDashboard } from '@objectifthunes/sandstone-sdk'`);
563
+ }
564
+ // ── Build the output ─────────────────────────────────────────────────────
565
+ const optionsStr = appOptions
566
+ .map((opt) => ` ${opt},`)
567
+ .join('\n');
568
+ const parts = [];
569
+ parts.push(imports.join('\n'));
570
+ parts.push('');
571
+ parts.push(inits.join('\n\n'));
572
+ parts.push('');
573
+ // Socket.io special export pattern
574
+ if (isSocketIo) {
575
+ if (devMode) {
576
+ imports.push(`import { createSocketIoRealtime } from ${devPath}`);
577
+ }
578
+ else {
579
+ imports.push(`import { createSocketIoRealtime } from '@objectifthunes/sandstone-sdk/socketio'`);
580
+ }
581
+ imports.push(`import type { Server as HttpServer } from 'node:http'`);
582
+ // Rebuild parts with updated imports
583
+ parts.length = 0;
584
+ parts.push(imports.join('\n'));
585
+ parts.push('');
586
+ parts.push(inits.join('\n\n'));
587
+ parts.push('');
588
+ if (devMode) {
589
+ parts.push(`export function createRealtime(server: HttpServer) {
590
+ return createSocketIoRealtime()
591
+ }`);
592
+ }
593
+ else {
594
+ parts.push(`export function createRealtime(server: HttpServer) {
595
+ return createSocketIoRealtime({ server })
596
+ }`);
597
+ }
598
+ parts.push('');
599
+ parts.push(`export const appOptions = {
600
+ ${optionsStr}
601
+ }`);
602
+ }
603
+ else {
604
+ parts.push(`export const app = await createApp({
605
+ ${optionsStr}
606
+ })`);
607
+ }
608
+ // ── Dashboard handler export ──────────────────────────────────────────────
609
+ if (config.dashboard) {
610
+ parts.push('');
611
+ if (isSocketIo) {
612
+ parts.push(`// Dashboard must be created after app is constructed in main.ts`);
613
+ parts.push(`export { createDashboard }`);
614
+ }
615
+ else {
616
+ parts.push(`export const dashboardHandler = createDashboard(app, {
617
+ secret: process.env.DASHBOARD_SECRET,
618
+ })`);
619
+ }
620
+ }
621
+ // ── Export extras that main.ts might need ─────────────────────────────────
622
+ if (config.storage) {
623
+ parts.push('');
624
+ parts.push('export { storage }');
625
+ }
626
+ if (config.cache) {
627
+ parts.push('');
628
+ parts.push('export { cache }');
629
+ }
630
+ if (config.auth === 'password+otp' || config.auth === 'all') {
631
+ parts.push('');
632
+ parts.push('export { passwordHasher }');
633
+ }
634
+ if ((config.auth === 'oauth+otp' || config.auth === 'all') && config.oauthProviders.length > 0) {
635
+ const oauthExports = config.oauthProviders.map((p) => `${p}OAuth`).join(', ');
636
+ parts.push('');
637
+ parts.push(`export { ${oauthExports} }`);
638
+ }
639
+ if (config.push === 'both') {
640
+ parts.push('');
641
+ parts.push(`// APNs push available for fan-out wrapper`);
642
+ parts.push('export { apnsPush }');
643
+ }
644
+ parts.push('');
645
+ return parts.join('\n');
646
+ }
647
+ //# sourceMappingURL=infrastructure.js.map