@objectifthunes/create-sandstone 0.2.0 → 1.1.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/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +31 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add-adapter.d.ts +2 -0
- package/dist/commands/add-adapter.d.ts.map +1 -0
- package/dist/commands/add-adapter.js +683 -0
- package/dist/commands/add-adapter.js.map +1 -0
- package/dist/commands/generate-adapter.d.ts +2 -0
- package/dist/commands/generate-adapter.d.ts.map +1 -0
- package/dist/commands/generate-adapter.js +467 -0
- package/dist/commands/generate-adapter.js.map +1 -0
- package/dist/commands/generate-entity.d.ts +2 -0
- package/dist/commands/generate-entity.d.ts.map +1 -0
- package/dist/commands/generate-entity.js +217 -0
- package/dist/commands/generate-entity.js.map +1 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +92 -25
- package/dist/generator.js.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/presets.d.ts +24 -4
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +42 -19
- package/dist/presets.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +181 -7
- package/dist/prompts.js.map +1 -1
- package/dist/templates/auth-test.d.ts +3 -0
- package/dist/templates/auth-test.d.ts.map +1 -0
- package/dist/templates/auth-test.js +52 -0
- package/dist/templates/auth-test.js.map +1 -0
- package/dist/templates/claude-md.d.ts.map +1 -1
- package/dist/templates/claude-md.js +472 -4
- package/dist/templates/claude-md.js.map +1 -1
- package/dist/templates/docker-compose.d.ts.map +1 -1
- package/dist/templates/docker-compose.js +42 -0
- package/dist/templates/docker-compose.js.map +1 -1
- package/dist/templates/entities-repositories.d.ts +3 -0
- package/dist/templates/entities-repositories.d.ts.map +1 -0
- package/dist/templates/entities-repositories.js +19 -0
- package/dist/templates/entities-repositories.js.map +1 -0
- package/dist/templates/entities-types.d.ts +3 -0
- package/dist/templates/entities-types.d.ts.map +1 -0
- package/dist/templates/entities-types.js +24 -0
- package/dist/templates/entities-types.js.map +1 -0
- package/dist/templates/env-example.d.ts.map +1 -1
- package/dist/templates/env-example.js +81 -2
- package/dist/templates/env-example.js.map +1 -1
- package/dist/templates/graphql-auth.d.ts +3 -0
- package/dist/templates/graphql-auth.d.ts.map +1 -0
- package/dist/templates/graphql-auth.js +160 -0
- package/dist/templates/graphql-auth.js.map +1 -0
- package/dist/templates/graphql-enums.d.ts +2 -0
- package/dist/templates/graphql-enums.d.ts.map +1 -0
- package/dist/templates/graphql-enums.js +28 -0
- package/dist/templates/graphql-enums.js.map +1 -0
- package/dist/templates/graphql-helpers.d.ts +2 -0
- package/dist/templates/graphql-helpers.d.ts.map +1 -0
- package/dist/templates/graphql-helpers.js +134 -0
- package/dist/templates/graphql-helpers.js.map +1 -0
- package/dist/templates/graphql-inputs.d.ts +2 -0
- package/dist/templates/graphql-inputs.d.ts.map +1 -0
- package/dist/templates/graphql-inputs.js +28 -0
- package/dist/templates/graphql-inputs.js.map +1 -0
- package/dist/templates/graphql-schema.d.ts +3 -0
- package/dist/templates/graphql-schema.d.ts.map +1 -0
- package/dist/templates/graphql-schema.js +33 -0
- package/dist/templates/graphql-schema.js.map +1 -0
- package/dist/templates/graphql-types.d.ts +3 -0
- package/dist/templates/graphql-types.d.ts.map +1 -0
- package/dist/templates/graphql-types.js +61 -0
- package/dist/templates/graphql-types.js.map +1 -0
- package/dist/templates/graphql-validation.d.ts +3 -0
- package/dist/templates/graphql-validation.d.ts.map +1 -0
- package/dist/templates/graphql-validation.js +37 -0
- package/dist/templates/graphql-validation.js.map +1 -0
- package/dist/templates/health-e2e-test.d.ts +3 -0
- package/dist/templates/health-e2e-test.d.ts.map +1 -0
- package/dist/templates/health-e2e-test.js +50 -0
- package/dist/templates/health-e2e-test.js.map +1 -0
- package/dist/templates/infrastructure.d.ts.map +1 -1
- package/dist/templates/infrastructure.js +433 -32
- package/dist/templates/infrastructure.js.map +1 -1
- package/dist/templates/main-express.d.ts.map +1 -1
- package/dist/templates/main-express.js +87 -8
- package/dist/templates/main-express.js.map +1 -1
- package/dist/templates/main-fastify.d.ts +3 -0
- package/dist/templates/main-fastify.d.ts.map +1 -0
- package/dist/templates/main-fastify.js +73 -0
- package/dist/templates/main-fastify.js.map +1 -0
- package/dist/templates/main-hono.d.ts.map +1 -1
- package/dist/templates/main-hono.js +45 -9
- package/dist/templates/main-hono.js.map +1 -1
- package/dist/templates/package-json.d.ts.map +1 -1
- package/dist/templates/package-json.js +67 -3
- package/dist/templates/package-json.js.map +1 -1
- package/dist/templates/test-setup.d.ts +1 -1
- package/dist/templates/test-setup.d.ts.map +1 -1
- package/dist/templates/test-setup.js +10 -39
- package/dist/templates/test-setup.js.map +1 -1
- package/dist/templates/vitest-config.d.ts +2 -0
- package/dist/templates/vitest-config.d.ts.map +1 -0
- package/dist/templates/vitest-config.js +13 -0
- package/dist/templates/vitest-config.js.map +1 -0
- package/dist/templates/vitest-e2e-config.d.ts +2 -0
- package/dist/templates/vitest-e2e-config.d.ts.map +1 -0
- package/dist/templates/vitest-e2e-config.js +16 -0
- package/dist/templates/vitest-e2e-config.js.map +1 -0
- package/dist/templates/workers-index.d.ts +3 -0
- package/dist/templates/workers-index.d.ts.map +1 -0
- package/dist/templates/workers-index.js +19 -0
- package/dist/templates/workers-index.js.map +1 -0
- package/package.json +6 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker-compose.d.ts","sourceRoot":"","sources":["../../src/templates/docker-compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"docker-compose.d.ts","sourceRoot":"","sources":["../../src/templates/docker-compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAmFnE"}
|
|
@@ -23,7 +23,49 @@ export function dockerComposeTemplate(config) {
|
|
|
23
23
|
- '1025:1025'
|
|
24
24
|
- '8025:8025'`);
|
|
25
25
|
}
|
|
26
|
+
if (config.cache === 'redis' || config.queue === 'bullmq') {
|
|
27
|
+
services.push(`
|
|
28
|
+
redis:
|
|
29
|
+
image: redis:7-alpine
|
|
30
|
+
ports:
|
|
31
|
+
- '6379:6379'
|
|
32
|
+
volumes:
|
|
33
|
+
- redisdata:/var/lib/redis/data`);
|
|
34
|
+
}
|
|
35
|
+
if (config.search === 'meilisearch') {
|
|
36
|
+
services.push(`
|
|
37
|
+
meilisearch:
|
|
38
|
+
image: getmeili/meilisearch:latest
|
|
39
|
+
ports:
|
|
40
|
+
- '7700:7700'
|
|
41
|
+
environment:
|
|
42
|
+
MEILI_ENV: development
|
|
43
|
+
MEILI_MASTER_KEY: development-master-key
|
|
44
|
+
volumes:
|
|
45
|
+
- meilidata:/meili_data`);
|
|
46
|
+
}
|
|
47
|
+
if (config.search === 'typesense') {
|
|
48
|
+
services.push(`
|
|
49
|
+
typesense:
|
|
50
|
+
image: typesense/typesense:27.1
|
|
51
|
+
ports:
|
|
52
|
+
- '8108:8108'
|
|
53
|
+
environment:
|
|
54
|
+
TYPESENSE_API_KEY: development-api-key
|
|
55
|
+
TYPESENSE_DATA_DIR: /data
|
|
56
|
+
volumes:
|
|
57
|
+
- typesensedata:/data`);
|
|
58
|
+
}
|
|
26
59
|
const volumes = ['volumes:', ' pgdata:'];
|
|
60
|
+
if (config.cache === 'redis' || config.queue === 'bullmq') {
|
|
61
|
+
volumes.push(' redisdata:');
|
|
62
|
+
}
|
|
63
|
+
if (config.search === 'meilisearch') {
|
|
64
|
+
volumes.push(' meilidata:');
|
|
65
|
+
}
|
|
66
|
+
if (config.search === 'typesense') {
|
|
67
|
+
volumes.push(' typesensedata:');
|
|
68
|
+
}
|
|
27
69
|
return `services:
|
|
28
70
|
${services.join('\n')}
|
|
29
71
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker-compose.js","sourceRoot":"","sources":["../../src/templates/docker-compose.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,QAAQ,CAAC,IAAI,CAAC;;;;;qBAKK,MAAM,CAAC,IAAI;;;;;;;;;iBASf,CAAC,CAAC;IAEjB,IAAI,MAAM,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC;;;;;oBAKE,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAE1C,OAAO;EACP,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;;EAEnB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;CACnB,CAAC;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"docker-compose.js","sourceRoot":"","sources":["../../src/templates/docker-compose.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,QAAQ,CAAC,IAAI,CAAC;;;;;qBAKK,MAAM,CAAC,IAAI;;;;;;;;;iBASf,CAAC,CAAC;IAEjB,IAAI,MAAM,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC;;;;;oBAKE,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC1D,QAAQ,CAAC,IAAI,CAAC;;;;;;sCAMoB,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;8BASY,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;4BASU,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAE1C,IAAI,MAAM,CAAC,KAAK,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACnC,CAAC;IAED,OAAO;EACP,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;;EAEnB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;CACnB,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entities-repositories.d.ts","sourceRoot":"","sources":["../../src/templates/entities-repositories.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAiB3E"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function entitiesRepositoriesTemplate(_config) {
|
|
2
|
+
return `// Define your createRepository() calls here.
|
|
3
|
+
// Each repository maps to a database table.
|
|
4
|
+
// All repo methods take db: Queryable as first arg (explicit dependency injection).
|
|
5
|
+
//
|
|
6
|
+
// import { createRepository } from '@objectifthunes/sandstone-sdk';
|
|
7
|
+
// import type { Client } from './types.js';
|
|
8
|
+
//
|
|
9
|
+
// export const clientRepo = createRepository<Client>('clients', { primaryKey: 'id' });
|
|
10
|
+
//
|
|
11
|
+
// Usage in resolvers:
|
|
12
|
+
// await clientRepo.findById(ctx.db, id);
|
|
13
|
+
// await clientRepo.paginate(ctx.db, { where: { user_id }, page, perPage });
|
|
14
|
+
// await clientRepo.create(tx, { user_id, name, email }); // inside transaction
|
|
15
|
+
// await clientRepo.update(ctx.db, id, { name, updated_at: new Date() });
|
|
16
|
+
// await clientRepo.delete(ctx.db, id);
|
|
17
|
+
`;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=entities-repositories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entities-repositories.js","sourceRoot":"","sources":["../../src/templates/entities-repositories.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,4BAA4B,CAAC,OAAsB;IACjE,OAAO;;;;;;;;;;;;;;;CAeR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entities-types.d.ts","sourceRoot":"","sources":["../../src/templates/entities-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAsBpE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function entitiesTypesTemplate(_config) {
|
|
2
|
+
return `// Define your entity TypeScript interfaces here.
|
|
3
|
+
// Each interface should match your database table columns exactly.
|
|
4
|
+
//
|
|
5
|
+
// Standards:
|
|
6
|
+
// - Every table: id (string/UUID), created_at (Date)
|
|
7
|
+
// - Every mutable table: updated_at (Date)
|
|
8
|
+
// - FKs reference auth_users(id) for user ownership
|
|
9
|
+
// - Money fields: number (stored as NUMERIC in DB, never float)
|
|
10
|
+
//
|
|
11
|
+
// Example:
|
|
12
|
+
// export interface Client {
|
|
13
|
+
// id: string;
|
|
14
|
+
// user_id: string;
|
|
15
|
+
// name: string;
|
|
16
|
+
// email: string;
|
|
17
|
+
// phone: string | null;
|
|
18
|
+
// address: Record<string, unknown> | null;
|
|
19
|
+
// created_at: Date;
|
|
20
|
+
// updated_at: Date;
|
|
21
|
+
// }
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=entities-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entities-types.js","sourceRoot":"","sources":["../../src/templates/entities-types.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,OAAsB;IAC1D,OAAO;;;;;;;;;;;;;;;;;;;;CAoBR,CAAC;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-example.d.ts","sourceRoot":"","sources":["../../src/templates/env-example.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"env-example.d.ts","sourceRoot":"","sources":["../../src/templates/env-example.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA2OhE"}
|
|
@@ -19,13 +19,27 @@ export function envExampleTemplate(config) {
|
|
|
19
19
|
'# Auth',
|
|
20
20
|
'JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars',
|
|
21
21
|
];
|
|
22
|
+
// Dedup trackers
|
|
23
|
+
let hasRedisUrl = false;
|
|
24
|
+
let hasAwsCredentials = false;
|
|
22
25
|
if (config.email === 'resend') {
|
|
23
26
|
lines.push('', '# Email (Resend)', 'RESEND_API_KEY=re_xxx', 'EMAIL_FROM=noreply@example.com');
|
|
24
27
|
}
|
|
25
28
|
else if (config.email === 'nodemailer') {
|
|
26
29
|
lines.push('', '# Email (SMTP)', 'SMTP_HOST=localhost', 'SMTP_PORT=1025', 'SMTP_USER=', 'SMTP_PASS=', 'EMAIL_FROM=noreply@example.com');
|
|
27
30
|
}
|
|
28
|
-
|
|
31
|
+
// OAuth providers — loop over config.oauthProviders
|
|
32
|
+
if (config.oauthProviders.length > 0) {
|
|
33
|
+
for (const provider of config.oauthProviders) {
|
|
34
|
+
const upper = provider.toUpperCase();
|
|
35
|
+
lines.push('', `# OAuth (${provider.charAt(0).toUpperCase() + provider.slice(1)})`, `${upper}_CLIENT_ID=`, `${upper}_CLIENT_SECRET=`, `${upper}_REDIRECT_URI=http://localhost:3000/auth/${provider}/callback`);
|
|
36
|
+
if (provider === 'apple') {
|
|
37
|
+
lines.push('APPLE_TEAM_ID=', 'APPLE_KEY_ID=', 'APPLE_PRIVATE_KEY=');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (config.auth === 'oauth+otp' || config.auth === 'all') {
|
|
42
|
+
// Fallback: no specific providers chosen but OAuth auth strategy is selected
|
|
29
43
|
lines.push('', '# OAuth (Google)', 'GOOGLE_CLIENT_ID=', 'GOOGLE_CLIENT_SECRET=', 'GOOGLE_REDIRECT_URI=http://localhost:3000/auth/google/callback');
|
|
30
44
|
}
|
|
31
45
|
if (config.payments === 'stripe') {
|
|
@@ -41,7 +55,8 @@ export function envExampleTemplate(config) {
|
|
|
41
55
|
lines.push('', '# Payments (Lemon Squeezy)', 'LEMONSQUEEZY_API_KEY=', 'LEMONSQUEEZY_WEBHOOK_SECRET=');
|
|
42
56
|
}
|
|
43
57
|
if (config.storage === 's3') {
|
|
44
|
-
lines.push('', '# Storage (S3)', 'S3_BUCKET=my-bucket', 'S3_REGION=us-east-1', '
|
|
58
|
+
lines.push('', '# Storage (S3)', 'S3_BUCKET=my-bucket', 'S3_REGION=us-east-1', 'AWS_ACCESS_KEY_ID=', 'AWS_SECRET_ACCESS_KEY=');
|
|
59
|
+
hasAwsCredentials = true;
|
|
45
60
|
}
|
|
46
61
|
else if (config.storage === 'r2') {
|
|
47
62
|
lines.push('', '# Storage (Cloudflare R2)', 'R2_ACCOUNT_ID=', 'R2_ACCESS_KEY_ID=', 'R2_SECRET_ACCESS_KEY=', 'R2_BUCKET=my-bucket');
|
|
@@ -52,6 +67,70 @@ export function envExampleTemplate(config) {
|
|
|
52
67
|
if (config.cache === 'upstash') {
|
|
53
68
|
lines.push('', '# Cache (Upstash Redis)', 'UPSTASH_REDIS_URL=', 'UPSTASH_REDIS_TOKEN=');
|
|
54
69
|
}
|
|
70
|
+
else if (config.cache === 'redis') {
|
|
71
|
+
lines.push('', '# Cache (Redis)', 'REDIS_URL=redis://localhost:6379');
|
|
72
|
+
hasRedisUrl = true;
|
|
73
|
+
}
|
|
74
|
+
// SMS
|
|
75
|
+
if (config.sms === 'twilio') {
|
|
76
|
+
lines.push('', '# SMS (Twilio)', 'TWILIO_ACCOUNT_SID=ACxxxxxxxxxx', 'TWILIO_AUTH_TOKEN=', 'TWILIO_FROM_NUMBER=+15551234567');
|
|
77
|
+
}
|
|
78
|
+
else if (config.sms === 'sns') {
|
|
79
|
+
lines.push('', '# SMS (AWS SNS)', 'AWS_REGION=us-east-1');
|
|
80
|
+
if (!hasAwsCredentials) {
|
|
81
|
+
lines.push('AWS_ACCESS_KEY_ID=', 'AWS_SECRET_ACCESS_KEY=');
|
|
82
|
+
hasAwsCredentials = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Push notifications
|
|
86
|
+
if (config.push === 'fcm' || config.push === 'both') {
|
|
87
|
+
lines.push('', '# Push (FCM)', 'FCM_PROJECT_ID=', 'FCM_CLIENT_EMAIL=', 'FCM_PRIVATE_KEY=');
|
|
88
|
+
}
|
|
89
|
+
if (config.push === 'apns' || config.push === 'both') {
|
|
90
|
+
lines.push('', '# Push (APNs)', 'APNS_KEY_ID=', 'APNS_TEAM_ID=', 'APNS_PRIVATE_KEY=', 'APNS_BUNDLE_ID=com.example.app');
|
|
91
|
+
}
|
|
92
|
+
// Realtime
|
|
93
|
+
if (config.realtime === 'pusher') {
|
|
94
|
+
lines.push('', '# Realtime (Pusher)', 'PUSHER_APP_ID=', 'PUSHER_KEY=', 'PUSHER_SECRET=', 'PUSHER_CLUSTER=us2');
|
|
95
|
+
}
|
|
96
|
+
else if (config.realtime === 'ably') {
|
|
97
|
+
lines.push('', '# Realtime (Ably)', 'ABLY_API_KEY=');
|
|
98
|
+
}
|
|
99
|
+
// socketio: no env vars needed
|
|
100
|
+
// Search
|
|
101
|
+
if (config.search === 'meilisearch') {
|
|
102
|
+
lines.push('', '# Search (Meilisearch)', 'MEILISEARCH_HOST=http://localhost:7700', 'MEILISEARCH_API_KEY=');
|
|
103
|
+
}
|
|
104
|
+
else if (config.search === 'typesense') {
|
|
105
|
+
lines.push('', '# Search (Typesense)', 'TYPESENSE_HOST=localhost', 'TYPESENSE_PORT=8108', 'TYPESENSE_API_KEY=');
|
|
106
|
+
}
|
|
107
|
+
// pg-search: no env vars, reuses DATABASE_URL
|
|
108
|
+
// Queue
|
|
109
|
+
if (config.queue === 'bullmq') {
|
|
110
|
+
if (!hasRedisUrl) {
|
|
111
|
+
lines.push('', '# Queue (BullMQ)', 'REDIS_URL=redis://localhost:6379');
|
|
112
|
+
hasRedisUrl = true;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
lines.push('', '# Queue (BullMQ)', '# Uses REDIS_URL defined above');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// pg-boss: no env vars, reuses DATABASE_URL
|
|
119
|
+
// Feature flags
|
|
120
|
+
if (config.flags === 'launchdarkly') {
|
|
121
|
+
lines.push('', '# Feature Flags (LaunchDarkly)', 'LAUNCHDARKLY_SDK_KEY=sdk-xxxxxxxxxx');
|
|
122
|
+
}
|
|
123
|
+
else if (config.flags === 'env-flags') {
|
|
124
|
+
lines.push('', '# Feature Flags (env-flags)', '# Set flags via environment variables with FLAG_ prefix, e.g.:', '# FLAG_MY_FEATURE=true');
|
|
125
|
+
}
|
|
126
|
+
// pg-flags: no env vars
|
|
127
|
+
// Tracing
|
|
128
|
+
if (config.tracing) {
|
|
129
|
+
lines.push('', '# Tracing (OpenTelemetry)', `OTEL_SERVICE_NAME=${config.name}`, 'OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318');
|
|
130
|
+
}
|
|
131
|
+
// Suppress unused variable warnings — both trackers are read above via conditionals
|
|
132
|
+
void hasRedisUrl;
|
|
133
|
+
void hasAwsCredentials;
|
|
55
134
|
lines.push('');
|
|
56
135
|
return lines.join('\n');
|
|
57
136
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-example.js","sourceRoot":"","sources":["../../src/templates/env-example.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,MAAqB;IACtD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO;YACL,UAAU;YACV,WAAW;YACX,gBAAgB;YAChB,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,MAAM,KAAK,GAAa;QACtB,UAAU;QACV,WAAW;QACX,sBAAsB;QACtB,gBAAgB;QAChB,EAAE;QACF,YAAY;QACZ,6DAA6D,GAAG,MAAM,CAAC,IAAI;QAC3E,EAAE;QACF,QAAQ;QACR,2DAA2D;KAC5D,CAAC;IAEF,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,gCAAgC,CAAC,CAAC;IAChG,CAAC;SAAM,IAAI,MAAM,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;QACzC,KAAK,CAAC,IAAI,CACR,EAAE,EACF,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,gCAAgC,CACjC,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"env-example.js","sourceRoot":"","sources":["../../src/templates/env-example.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-auth.d.ts","sourceRoot":"","sources":["../../src/templates/graphql-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA8KjE"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
export function graphqlAuthTemplate(config) {
|
|
2
|
+
if (!config.auth) {
|
|
3
|
+
return `// Auth is not enabled for this project.
|
|
4
|
+
// To add auth, re-run create-sandstone with an auth strategy.
|
|
5
|
+
export const authQueries = {};
|
|
6
|
+
export const authMutations = {};
|
|
7
|
+
`;
|
|
8
|
+
}
|
|
9
|
+
const hasPassword = config.auth === 'password+otp' || config.auth === 'all';
|
|
10
|
+
const hasOAuth = (config.auth === 'oauth+otp' || config.auth === 'all') && config.oauthProviders.length > 0;
|
|
11
|
+
const imports = [];
|
|
12
|
+
const queryFields = [];
|
|
13
|
+
const mutationFields = [];
|
|
14
|
+
// ── Imports ──────────────────────────────────────────────────────────────
|
|
15
|
+
imports.push(`import {
|
|
16
|
+
GraphQLObjectType,
|
|
17
|
+
GraphQLString,
|
|
18
|
+
GraphQLNonNull,
|
|
19
|
+
GraphQLBoolean,
|
|
20
|
+
} from '@objectifthunes/sandstone-sdk';`);
|
|
21
|
+
imports.push(`import type { GraphQLContext } from '@objectifthunes/sandstone-sdk';`);
|
|
22
|
+
imports.push(`import { requireAuth } from './helpers.js';`);
|
|
23
|
+
imports.push(`import { AuthResultType, MeType } from './types.js';`);
|
|
24
|
+
imports.push(`import { app as sandstoneApp } from '../infrastructure.js';`);
|
|
25
|
+
if (hasPassword) {
|
|
26
|
+
imports.push(`import { validate } from '@objectifthunes/sandstone-sdk';`);
|
|
27
|
+
imports.push(`import { RegisterSchema } from './validation.js';`);
|
|
28
|
+
}
|
|
29
|
+
// ── me query ─────────────────────────────────────────────────────────────
|
|
30
|
+
queryFields.push(` me: {
|
|
31
|
+
type: MeType,
|
|
32
|
+
resolve: async (_parent: unknown, _args: unknown, ctx: GraphQLContext) => {
|
|
33
|
+
const user = requireAuth(ctx);
|
|
34
|
+
const fullUser = await sandstoneApp.auth!.getUser(user.sub);
|
|
35
|
+
return fullUser;
|
|
36
|
+
},
|
|
37
|
+
}`);
|
|
38
|
+
// ── sendCode mutation ────────────────────────────────────────────────────
|
|
39
|
+
mutationFields.push(` sendCode: {
|
|
40
|
+
type: GraphQLBoolean,
|
|
41
|
+
args: {
|
|
42
|
+
email: { type: new GraphQLNonNull(GraphQLString) },
|
|
43
|
+
},
|
|
44
|
+
resolve: async (_parent: unknown, args: Record<string, string>) => {
|
|
45
|
+
await sandstoneApp.auth!.sendCode(args.email);
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
}`);
|
|
49
|
+
// ── verifyCode mutation ──────────────────────────────────────────────────
|
|
50
|
+
mutationFields.push(` verifyCode: {
|
|
51
|
+
type: AuthResultType,
|
|
52
|
+
args: {
|
|
53
|
+
email: { type: new GraphQLNonNull(GraphQLString) },
|
|
54
|
+
code: { type: new GraphQLNonNull(GraphQLString) },
|
|
55
|
+
},
|
|
56
|
+
resolve: async (_parent: unknown, args: Record<string, string>) => {
|
|
57
|
+
const result = await sandstoneApp.auth!.verifyCode(args.email, args.code);
|
|
58
|
+
return result;
|
|
59
|
+
},
|
|
60
|
+
}`);
|
|
61
|
+
// ── register mutation (password auth) ────────────────────────────────────
|
|
62
|
+
if (hasPassword) {
|
|
63
|
+
mutationFields.push(` register: {
|
|
64
|
+
type: AuthResultType,
|
|
65
|
+
args: {
|
|
66
|
+
email: { type: new GraphQLNonNull(GraphQLString) },
|
|
67
|
+
password: { type: new GraphQLNonNull(GraphQLString) },
|
|
68
|
+
},
|
|
69
|
+
resolve: async (_parent: unknown, args: Record<string, string>) => {
|
|
70
|
+
validate(RegisterSchema, { email: args.email, password: args.password });
|
|
71
|
+
const result = await sandstoneApp.auth!.register({ email: args.email, password: args.password });
|
|
72
|
+
return result;
|
|
73
|
+
},
|
|
74
|
+
}`);
|
|
75
|
+
}
|
|
76
|
+
// ── loginWithPassword mutation (password auth) ───────────────────────────
|
|
77
|
+
if (hasPassword) {
|
|
78
|
+
mutationFields.push(` loginWithPassword: {
|
|
79
|
+
type: AuthResultType,
|
|
80
|
+
args: {
|
|
81
|
+
email: { type: new GraphQLNonNull(GraphQLString) },
|
|
82
|
+
password: { type: new GraphQLNonNull(GraphQLString) },
|
|
83
|
+
},
|
|
84
|
+
resolve: async (_parent: unknown, args: Record<string, string>) => {
|
|
85
|
+
const result = await sandstoneApp.auth!.loginWithPassword({ email: args.email, password: args.password });
|
|
86
|
+
return result;
|
|
87
|
+
},
|
|
88
|
+
}`);
|
|
89
|
+
}
|
|
90
|
+
// ── getOAuthUrl mutation (OAuth) ─────────────────────────────────────────
|
|
91
|
+
if (hasOAuth) {
|
|
92
|
+
mutationFields.push(` getOAuthUrl: {
|
|
93
|
+
type: new GraphQLNonNull(GraphQLString),
|
|
94
|
+
args: {
|
|
95
|
+
provider: { type: new GraphQLNonNull(GraphQLString) },
|
|
96
|
+
redirectUri: { type: new GraphQLNonNull(GraphQLString) },
|
|
97
|
+
},
|
|
98
|
+
resolve: async (_parent: unknown, args: Record<string, string>) => {
|
|
99
|
+
const { url } = await sandstoneApp.auth!.getOAuthUrl(args.provider, { redirectUri: args.redirectUri });
|
|
100
|
+
return url;
|
|
101
|
+
},
|
|
102
|
+
}`);
|
|
103
|
+
}
|
|
104
|
+
// ── handleOAuthCallback mutation (OAuth) ─────────────────────────────────
|
|
105
|
+
if (hasOAuth) {
|
|
106
|
+
mutationFields.push(` handleOAuthCallback: {
|
|
107
|
+
type: AuthResultType,
|
|
108
|
+
args: {
|
|
109
|
+
provider: { type: new GraphQLNonNull(GraphQLString) },
|
|
110
|
+
code: { type: new GraphQLNonNull(GraphQLString) },
|
|
111
|
+
state: { type: new GraphQLNonNull(GraphQLString) },
|
|
112
|
+
redirectUri: { type: new GraphQLNonNull(GraphQLString) },
|
|
113
|
+
},
|
|
114
|
+
resolve: async (_parent: unknown, args: Record<string, string>) => {
|
|
115
|
+
const result = await sandstoneApp.auth!.handleOAuthCallback(
|
|
116
|
+
args.provider,
|
|
117
|
+
{ code: args.code, redirectUri: args.redirectUri, state: args.state },
|
|
118
|
+
);
|
|
119
|
+
return result;
|
|
120
|
+
},
|
|
121
|
+
}`);
|
|
122
|
+
}
|
|
123
|
+
// ── refreshToken mutation ────────────────────────────────────────────────
|
|
124
|
+
mutationFields.push(` refreshToken: {
|
|
125
|
+
type: AuthResultType,
|
|
126
|
+
args: {
|
|
127
|
+
refreshToken: { type: new GraphQLNonNull(GraphQLString) },
|
|
128
|
+
},
|
|
129
|
+
resolve: async (_parent: unknown, args: Record<string, string>) => {
|
|
130
|
+
const result = await sandstoneApp.auth!.refresh(args.refreshToken);
|
|
131
|
+
return result;
|
|
132
|
+
},
|
|
133
|
+
}`);
|
|
134
|
+
// ── logout mutation ──────────────────────────────────────────────────────
|
|
135
|
+
mutationFields.push(` logout: {
|
|
136
|
+
type: GraphQLBoolean,
|
|
137
|
+
args: {
|
|
138
|
+
sessionId: { type: new GraphQLNonNull(GraphQLString) },
|
|
139
|
+
},
|
|
140
|
+
resolve: async (_parent: unknown, args: Record<string, string>, ctx: GraphQLContext) => {
|
|
141
|
+
requireAuth(ctx);
|
|
142
|
+
await sandstoneApp.auth!.logout(args.sessionId);
|
|
143
|
+
return true;
|
|
144
|
+
},
|
|
145
|
+
}`);
|
|
146
|
+
// ── Build output ─────────────────────────────────────────────────────────
|
|
147
|
+
const parts = [];
|
|
148
|
+
parts.push(imports.join('\n'));
|
|
149
|
+
parts.push('');
|
|
150
|
+
parts.push(`export const authQueries = {`);
|
|
151
|
+
parts.push(queryFields.join(',\n'));
|
|
152
|
+
parts.push(`};`);
|
|
153
|
+
parts.push('');
|
|
154
|
+
parts.push(`export const authMutations = {`);
|
|
155
|
+
parts.push(mutationFields.join(',\n'));
|
|
156
|
+
parts.push(`};`);
|
|
157
|
+
parts.push('');
|
|
158
|
+
return parts.join('\n');
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=graphql-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-auth.js","sourceRoot":"","sources":["../../src/templates/graphql-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-enums.d.ts","sourceRoot":"","sources":["../../src/templates/graphql-enums.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,IAAI,MAAM,CA0B7C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function graphqlEnumsTemplate() {
|
|
2
|
+
return `// Add your GraphQL enum types here.
|
|
3
|
+
// Enums enforce a fixed set of values at the GraphQL schema level.
|
|
4
|
+
//
|
|
5
|
+
// Example:
|
|
6
|
+
// import { GraphQLEnumType } from '@objectifthunes/sandstone-sdk';
|
|
7
|
+
//
|
|
8
|
+
// export const InvoiceStatusEnum = new GraphQLEnumType({
|
|
9
|
+
// name: 'InvoiceStatus',
|
|
10
|
+
// values: {
|
|
11
|
+
// DRAFT: { value: 'draft' },
|
|
12
|
+
// SENT: { value: 'sent' },
|
|
13
|
+
// PAID: { value: 'paid' },
|
|
14
|
+
// VOID: { value: 'void' },
|
|
15
|
+
// },
|
|
16
|
+
// });
|
|
17
|
+
//
|
|
18
|
+
// export const CurrencyEnum = new GraphQLEnumType({
|
|
19
|
+
// name: 'Currency',
|
|
20
|
+
// values: {
|
|
21
|
+
// EUR: { value: 'EUR' },
|
|
22
|
+
// USD: { value: 'USD' },
|
|
23
|
+
// GBP: { value: 'GBP' },
|
|
24
|
+
// },
|
|
25
|
+
// });
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=graphql-enums.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-enums.js","sourceRoot":"","sources":["../../src/templates/graphql-enums.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwBR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-helpers.d.ts","sourceRoot":"","sources":["../../src/templates/graphql-helpers.ts"],"names":[],"mappings":"AAAA,wBAAgB,sBAAsB,IAAI,MAAM,CAoI/C"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
export function graphqlHelpersTemplate() {
|
|
2
|
+
return `import { UnauthorizedError, NotFoundError } from '@objectifthunes/sandstone-sdk';
|
|
3
|
+
import type { GraphQLContext } from '@objectifthunes/sandstone-sdk';
|
|
4
|
+
import { app as sandstoneApp } from '../infrastructure.js';
|
|
5
|
+
|
|
6
|
+
// ── 1. Auth guard (implemented) ──────────────────────────────────────────────
|
|
7
|
+
export function requireAuth(ctx: GraphQLContext) {
|
|
8
|
+
if (!ctx.user) throw new UnauthorizedError('Authentication required');
|
|
9
|
+
return ctx.user;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// ── 2. Ownership check pattern ───────────────────────────────────────────────
|
|
13
|
+
// Checks authz adapter first, then falls back to manual user_id comparison.
|
|
14
|
+
//
|
|
15
|
+
// export async function getOwnedEntity(ctx: GraphQLContext, userId: string, entityId: string) {
|
|
16
|
+
// const entity = await entityRepo.findById(ctx.db, entityId);
|
|
17
|
+
// if (!entity) throw new NotFoundError('Entity', entityId);
|
|
18
|
+
// if (sandstoneApp.authz) {
|
|
19
|
+
// const allowed = await sandstoneApp.authz.can(userId, 'read', 'entity', entityId);
|
|
20
|
+
// if (allowed) return entity;
|
|
21
|
+
// }
|
|
22
|
+
// if (entity.user_id !== userId) throw new NotFoundError('Entity', entityId);
|
|
23
|
+
// return entity;
|
|
24
|
+
// }
|
|
25
|
+
|
|
26
|
+
// ── 3. Cache read-through pattern ────────────────────────────────────────────
|
|
27
|
+
// Reads from cache first, falls back to DB, then populates cache with TTL.
|
|
28
|
+
// Always invalidate on mutation.
|
|
29
|
+
//
|
|
30
|
+
// export async function getCachedEntities(userId: string, page: number, perPage: number) {
|
|
31
|
+
// const cacheKey = \`entities:\${userId}:\${page}:\${perPage}\`;
|
|
32
|
+
// if (sandstoneApp.cache) {
|
|
33
|
+
// const cached = await sandstoneApp.cache.get(cacheKey);
|
|
34
|
+
// if (cached) return cached;
|
|
35
|
+
// }
|
|
36
|
+
// const result = await entityRepo.paginate(sandstoneApp.db, {
|
|
37
|
+
// where: { user_id: userId },
|
|
38
|
+
// page,
|
|
39
|
+
// perPage,
|
|
40
|
+
// });
|
|
41
|
+
// if (sandstoneApp.cache) {
|
|
42
|
+
// await sandstoneApp.cache.set(cacheKey, result, 60); // 60s TTL
|
|
43
|
+
// }
|
|
44
|
+
// return result;
|
|
45
|
+
// }
|
|
46
|
+
//
|
|
47
|
+
// export async function invalidateEntityCache(userId: string) {
|
|
48
|
+
// if (sandstoneApp.cache) {
|
|
49
|
+
// await sandstoneApp.cache.delete(\`entities:\${userId}:*\`);
|
|
50
|
+
// }
|
|
51
|
+
// }
|
|
52
|
+
|
|
53
|
+
// ── 4. Search indexing pattern ───────────────────────────────────────────────
|
|
54
|
+
// Index documents on create/update, remove on delete.
|
|
55
|
+
//
|
|
56
|
+
// export async function indexEntityInSearch(entity: { id: string; name: string; status: string; user_id: string }) {
|
|
57
|
+
// if (sandstoneApp.search) {
|
|
58
|
+
// await sandstoneApp.search.index('entity_idx', [{
|
|
59
|
+
// id: entity.id,
|
|
60
|
+
// document: {
|
|
61
|
+
// name: entity.name,
|
|
62
|
+
// status: entity.status,
|
|
63
|
+
// user_id: entity.user_id,
|
|
64
|
+
// },
|
|
65
|
+
// }]);
|
|
66
|
+
// }
|
|
67
|
+
// }
|
|
68
|
+
//
|
|
69
|
+
// export async function removeEntityFromSearch(entityId: string) {
|
|
70
|
+
// if (sandstoneApp.search) {
|
|
71
|
+
// await sandstoneApp.search.delete('entity_idx', [entityId]);
|
|
72
|
+
// }
|
|
73
|
+
// }
|
|
74
|
+
|
|
75
|
+
// ── 5. Queue enqueue pattern ─────────────────────────────────────────────────
|
|
76
|
+
// Offload heavy work to background workers.
|
|
77
|
+
//
|
|
78
|
+
// export async function enqueueEntityJob(entityId: string, action: string) {
|
|
79
|
+
// if (sandstoneApp.queue) {
|
|
80
|
+
// await sandstoneApp.queue.enqueue('entity-jobs', { entityId, action }, {
|
|
81
|
+
// maxAttempts: 3,
|
|
82
|
+
// backoff: 'exponential',
|
|
83
|
+
// backoffDelayMs: 1000,
|
|
84
|
+
// });
|
|
85
|
+
// }
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
// ── 6. Audit logging pattern ─────────────────────────────────────────────────
|
|
89
|
+
// Log every mutation for compliance and debugging.
|
|
90
|
+
//
|
|
91
|
+
// export async function auditLog(actor: string, action: string, resource: string, resourceId: string) {
|
|
92
|
+
// if (sandstoneApp.audit) {
|
|
93
|
+
// await sandstoneApp.audit.log({
|
|
94
|
+
// actor,
|
|
95
|
+
// action,
|
|
96
|
+
// resource,
|
|
97
|
+
// resourceId,
|
|
98
|
+
// });
|
|
99
|
+
// }
|
|
100
|
+
// }
|
|
101
|
+
|
|
102
|
+
// ── 7. Tracing pattern ──────────────────────────────────────────────────────
|
|
103
|
+
// Wrap business operations in spans for observability.
|
|
104
|
+
//
|
|
105
|
+
// export async function withTrace<T>(name: string, fn: () => Promise<T>): Promise<T> {
|
|
106
|
+
// return sandstoneApp.tracer.withSpan(name, async (span) => {
|
|
107
|
+
// try {
|
|
108
|
+
// const result = await fn();
|
|
109
|
+
// span.setStatus('ok');
|
|
110
|
+
// return result;
|
|
111
|
+
// } catch (err) {
|
|
112
|
+
// span.setStatus('error', err instanceof Error ? err.message : 'unknown');
|
|
113
|
+
// throw err;
|
|
114
|
+
// }
|
|
115
|
+
// });
|
|
116
|
+
// }
|
|
117
|
+
|
|
118
|
+
// ── 8. Feature flag pattern ─────────────────────────────────────────────────
|
|
119
|
+
// Branch logic based on feature flags with user-level targeting.
|
|
120
|
+
//
|
|
121
|
+
// export async function isFeatureEnabled(flag: string, userId?: string): Promise<boolean> {
|
|
122
|
+
// if (!sandstoneApp.flags) return false;
|
|
123
|
+
// return sandstoneApp.flags.isEnabled(flag, userId ? { userId } : undefined);
|
|
124
|
+
// }
|
|
125
|
+
//
|
|
126
|
+
// Usage in a resolver:
|
|
127
|
+
// const useNewFlow = await isFeatureEnabled('new_checkout_flow', user.sub);
|
|
128
|
+
// if (useNewFlow) {
|
|
129
|
+
// return handleNewCheckoutFlow(args);
|
|
130
|
+
// }
|
|
131
|
+
// return handleLegacyCheckoutFlow(args);
|
|
132
|
+
`;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=graphql-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-helpers.js","sourceRoot":"","sources":["../../src/templates/graphql-helpers.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,sBAAsB;IACpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkIR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-inputs.d.ts","sourceRoot":"","sources":["../../src/templates/graphql-inputs.ts"],"names":[],"mappings":"AAAA,wBAAgB,qBAAqB,IAAI,MAAM,CA0B9C"}
|