@docker-harpoon/prisma 0.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.
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @harpoon/prisma
3
+ *
4
+ * Prisma binding for Harpoon.
5
+ *
6
+ * Provides:
7
+ * - PrismaBinding: Attaches to database containers for connection strings and migrations
8
+ * - Config patchers for Prisma v7 LibSQL support
9
+ * - Dockerfile transformers for prisma commands in monorepos
10
+ */
11
+ import { Effect } from 'effect';
12
+ import type { Binding, PatchContext, PatchResult, DockerfileInstruction, TransformContext } from '@harpoon/core';
13
+ export { prismaV7LibSqlPatcher } from './patchers/prisma-v7-libsql';
14
+ export { prismaTransformers, prismaPackageManagerTransformer, prismaConditionalSeedTransformer, } from './transformers/prisma';
15
+ export interface PrismaBindingOptions {
16
+ /** Run prisma migrate deploy on container start */
17
+ migrate?: boolean;
18
+ /** Run prisma db push on container start */
19
+ push?: boolean;
20
+ /** Run prisma generate on container start */
21
+ generate?: boolean;
22
+ /** Path to prisma schema (default: ./prisma/schema.prisma) */
23
+ schema?: string;
24
+ /** Custom database URL override */
25
+ databaseUrl?: string;
26
+ }
27
+ export interface DatabaseLike {
28
+ connectionString: string;
29
+ }
30
+ /**
31
+ * Creates a Prisma binding that attaches to a database container.
32
+ *
33
+ * Provides:
34
+ * - DATABASE_URL environment variable
35
+ * - Optional migration/generation on container start
36
+ * - Dockerfile transformations for Prisma commands
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { database, Container } from '@harpoon/core';
41
+ * import { PrismaBinding } from '@harpoon/prisma';
42
+ *
43
+ * const db = await database("postgres", {
44
+ * image: "postgres:15",
45
+ * env: { POSTGRES_PASSWORD: "secret" },
46
+ * });
47
+ *
48
+ * const app = await Container("app", {
49
+ * image: appImage,
50
+ * bindings: {
51
+ * DB: PrismaBinding(db, { migrate: true }),
52
+ * },
53
+ * });
54
+ * ```
55
+ */
56
+ /**
57
+ * Extended binding interface that includes build-time capabilities.
58
+ * Unlike BuildBinding, this allows a resource to be attached.
59
+ */
60
+ interface PrismaBindingResult<TDb> extends Binding<TDb, {
61
+ DATABASE_URL: string;
62
+ }> {
63
+ transformDockerfile(instructions: readonly DockerfileInstruction[], ctx: TransformContext): readonly DockerfileInstruction[];
64
+ patchConfig(ctx: PatchContext): Effect.Effect<PatchResult, Error>;
65
+ }
66
+ export declare function PrismaBinding<TDb extends DatabaseLike>(db: TDb, options?: PrismaBindingOptions): PrismaBindingResult<TDb>;
67
+ /**
68
+ * Register all Prisma patchers and transformers with Harpoon's registries.
69
+ *
70
+ * Call this once at application startup if you want the patchers and
71
+ * transformers to be available globally.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * import { registerPrismaPlugins } from '@harpoon/prisma';
76
+ *
77
+ * registerPrismaPlugins();
78
+ * ```
79
+ */
80
+ export declare function registerPrismaPlugins(): void;
81
+ export default PrismaBinding;
82
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,gBAAgB,EAGjB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EACL,kBAAkB,EAClB,+BAA+B,EAC/B,gCAAgC,GACjC,MAAM,uBAAuB,CAAC;AAI/B,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH;;;GAGG;AACH,UAAU,mBAAmB,CAAC,GAAG,CAAE,SAAQ,OAAO,CAAC,GAAG,EAAE;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;IAC/E,mBAAmB,CACjB,YAAY,EAAE,SAAS,qBAAqB,EAAE,EAC9C,GAAG,EAAE,gBAAgB,GACpB,SAAS,qBAAqB,EAAE,CAAC;IACpC,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;CACnE;AAED,wBAAgB,aAAa,CAAC,GAAG,SAAS,YAAY,EACpD,EAAE,EAAE,GAAG,EACP,OAAO,GAAE,oBAAyB,GACjC,mBAAmB,CAAC,GAAG,CAAC,CA6E1B;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAiB5C;AAED,eAAe,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @harpoon/prisma
3
+ *
4
+ * Prisma binding for Harpoon.
5
+ *
6
+ * Provides:
7
+ * - PrismaBinding: Attaches to database containers for connection strings and migrations
8
+ * - Config patchers for Prisma v7 LibSQL support
9
+ * - Dockerfile transformers for prisma commands in monorepos
10
+ */
11
+ import { Effect } from 'effect';
12
+ // Re-export patchers and transformers
13
+ export { prismaV7LibSqlPatcher } from './patchers/prisma-v7-libsql';
14
+ export { prismaTransformers, prismaPackageManagerTransformer, prismaConditionalSeedTransformer, } from './transformers/prisma';
15
+ export function PrismaBinding(db, options = {}) {
16
+ const databaseUrl = options.databaseUrl ?? db.connectionString;
17
+ return {
18
+ type: 'prisma',
19
+ resource: db,
20
+ getEnv() {
21
+ return {
22
+ DATABASE_URL: databaseUrl,
23
+ };
24
+ },
25
+ onStart(_container) {
26
+ return Effect.gen(function* () {
27
+ if (options.generate) {
28
+ console.log(`[PrismaBinding] Running prisma generate...`);
29
+ // In a real implementation, this would exec into the container
30
+ // For now, this is a placeholder for the hook
31
+ }
32
+ if (options.migrate) {
33
+ console.log(`[PrismaBinding] Running prisma migrate deploy...`);
34
+ // In a real implementation, this would exec into the container
35
+ }
36
+ if (options.push) {
37
+ console.log(`[PrismaBinding] Running prisma db push...`);
38
+ // In a real implementation, this would exec into the container
39
+ }
40
+ });
41
+ },
42
+ // Build-time transformations (for when building images with Prisma)
43
+ transformDockerfile(instructions, ctx) {
44
+ // Import and apply prisma transformers
45
+ const { prismaTransformers } = require('./transformers/prisma');
46
+ let result = [...instructions];
47
+ for (const transformer of prismaTransformers) {
48
+ result = result.flatMap((inst) => {
49
+ if (!transformer.handlesTypes.includes(inst.type)) {
50
+ return [inst];
51
+ }
52
+ if (!transformer.matches(inst, ctx)) {
53
+ return [inst];
54
+ }
55
+ const transformed = transformer.transform(inst, ctx);
56
+ return Array.isArray(transformed) ? [...transformed] : [transformed];
57
+ });
58
+ }
59
+ return result;
60
+ },
61
+ patchConfig(ctx) {
62
+ const { prismaV7LibSqlPatcher } = require('./patchers/prisma-v7-libsql');
63
+ return Effect.gen(function* () {
64
+ const shouldApply = yield* prismaV7LibSqlPatcher.shouldApply(ctx);
65
+ if (!shouldApply) {
66
+ return {
67
+ applied: false,
68
+ modifiedFiles: [],
69
+ description: 'Prisma v7 not detected',
70
+ };
71
+ }
72
+ return yield* prismaV7LibSqlPatcher.apply(ctx);
73
+ });
74
+ },
75
+ };
76
+ }
77
+ // ============ Registration Helpers ============
78
+ /**
79
+ * Register all Prisma patchers and transformers with Harpoon's registries.
80
+ *
81
+ * Call this once at application startup if you want the patchers and
82
+ * transformers to be available globally.
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * import { registerPrismaPlugins } from '@harpoon/prisma';
87
+ *
88
+ * registerPrismaPlugins();
89
+ * ```
90
+ */
91
+ export function registerPrismaPlugins() {
92
+ // Dynamic imports to avoid circular dependencies
93
+ const core = require('@harpoon/core');
94
+ const { prismaV7LibSqlPatcher } = require('./patchers/prisma-v7-libsql');
95
+ const { prismaTransformers } = require('./transformers/prisma');
96
+ // Register config patcher
97
+ core.registerConfigPatcher(prismaV7LibSqlPatcher);
98
+ // Register transformers
99
+ for (const transformer of prismaTransformers) {
100
+ core.registerTransformer(transformer);
101
+ }
102
+ }
103
+ export default PrismaBinding;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Prisma V7 LibSQL Config Patcher
3
+ *
4
+ * Patches Prisma v7 libsql-config.ts to include authToken support.
5
+ * Also patches prisma.config.ts for LibSQL configuration.
6
+ */
7
+ import type { ConfigPatcher } from '@harpoon/core';
8
+ /**
9
+ * Prisma V7 LibSQL patcher.
10
+ *
11
+ * Patches:
12
+ * - prisma/libsql-config.ts: Adds SQL_TOKEN/authToken support
13
+ * - prisma.config.ts: Ensures LibSQL configuration is correct
14
+ */
15
+ export declare const prismaV7LibSqlPatcher: ConfigPatcher;
16
+ export default prismaV7LibSqlPatcher;
17
+ //# sourceMappingURL=prisma-v7-libsql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma-v7-libsql.d.ts","sourceRoot":"","sources":["../../src/patchers/prisma-v7-libsql.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,aAAa,EAA6B,MAAM,eAAe,CAAC;AAsE9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,EAAE,aA0DnC,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Prisma V7 LibSQL Config Patcher
3
+ *
4
+ * Patches Prisma v7 libsql-config.ts to include authToken support.
5
+ * Also patches prisma.config.ts for LibSQL configuration.
6
+ */
7
+ import { Effect } from 'effect';
8
+ import * as fs from 'fs/promises';
9
+ import * as path from 'path';
10
+ import { ConfigPatchError } from '@harpoon/core';
11
+ // ============ Helper Functions ============
12
+ /**
13
+ * Check if the project uses Prisma v7.
14
+ */
15
+ async function isPrismaV7(packageJsonPath) {
16
+ try {
17
+ const appPackageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
18
+ const prismaVersion = appPackageJson.dependencies?.['prisma'] ||
19
+ appPackageJson.devDependencies?.['prisma'];
20
+ return !!(prismaVersion && prismaVersion.startsWith('^7.'));
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ /**
27
+ * Patch the libsql-config.ts file to include authToken support.
28
+ */
29
+ async function patchLibSqlConfig(libsqlConfigPath, appName) {
30
+ try {
31
+ const libsqlConfig = await fs.readFile(libsqlConfigPath, 'utf-8');
32
+ const patched = libsqlConfig.replace(/^(\s+)return \{\s*url: env\.DATABASE_URL,\s*\};$/m, (match, indent) => {
33
+ return `${indent}return {
34
+ ${indent} url: env.DATABASE_URL,
35
+ ${indent} ...(env.SQL_TOKEN ? { authToken: env.SQL_TOKEN } : {}),
36
+ ${indent}};`;
37
+ });
38
+ if (patched !== libsqlConfig) {
39
+ await fs.writeFile(libsqlConfigPath, patched);
40
+ console.log(`[ConfigPatcher] Patched Prisma v7 libsql-config.ts for ${appName}`);
41
+ return { patched: true, path: libsqlConfigPath };
42
+ }
43
+ }
44
+ catch {
45
+ // File doesn't exist or error, skip silently
46
+ }
47
+ return { patched: false, path: libsqlConfigPath };
48
+ }
49
+ /**
50
+ * Check if prisma.config.ts needs patching.
51
+ */
52
+ async function checkPrismaConfig(prismaConfigPath) {
53
+ try {
54
+ const prismaConfig = await fs.readFile(prismaConfigPath, 'utf-8');
55
+ // Check if it's already using LibSQL properly
56
+ return !prismaConfig.includes('new PrismaLibSql(createLibSqlConfig())');
57
+ }
58
+ catch {
59
+ return false;
60
+ }
61
+ }
62
+ // ============ Patcher Implementation ============
63
+ /**
64
+ * Prisma V7 LibSQL patcher.
65
+ *
66
+ * Patches:
67
+ * - prisma/libsql-config.ts: Adds SQL_TOKEN/authToken support
68
+ * - prisma.config.ts: Ensures LibSQL configuration is correct
69
+ */
70
+ export const prismaV7LibSqlPatcher = {
71
+ name: 'prisma-v7-libsql',
72
+ description: 'Patches Prisma v7 LibSQL configuration for authToken support',
73
+ shouldApply: (ctx) => Effect.tryPromise({
74
+ try: () => isPrismaV7(ctx.packageJsonPath),
75
+ catch: (error) => new ConfigPatchError('prisma-v7-libsql', `Failed to check Prisma version: ${error}`, error instanceof Error ? error : undefined),
76
+ }),
77
+ apply: (ctx) => Effect.tryPromise({
78
+ try: async () => {
79
+ const modifiedFiles = [];
80
+ // Patch libsql-config.ts
81
+ const libsqlConfigPath = path.join(ctx.appDir, 'prisma', 'libsql-config.ts');
82
+ const libsqlResult = await patchLibSqlConfig(libsqlConfigPath, ctx.appName);
83
+ if (libsqlResult.patched) {
84
+ modifiedFiles.push(libsqlResult.path);
85
+ }
86
+ // Check prisma.config.ts (only report, don't modify)
87
+ const prismaConfigPath = path.join(ctx.appDir, 'prisma.config.ts');
88
+ const needsPrismaPatch = await checkPrismaConfig(prismaConfigPath);
89
+ if (needsPrismaPatch) {
90
+ console.log(`[ConfigPatcher] prisma.config.ts may need manual review for ${ctx.appName}`);
91
+ }
92
+ return {
93
+ applied: modifiedFiles.length > 0,
94
+ modifiedFiles: Object.freeze(modifiedFiles),
95
+ description: modifiedFiles.length > 0
96
+ ? `Patched ${modifiedFiles.length} file(s) for Prisma v7 LibSQL support`
97
+ : 'No files needed patching',
98
+ };
99
+ },
100
+ catch: (error) => new ConfigPatchError('prisma-v7-libsql', `Failed to apply Prisma v7 LibSQL patches: ${error}`, error instanceof Error ? error : undefined),
101
+ }),
102
+ };
103
+ export default prismaV7LibSqlPatcher;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Prisma Dockerfile Transformers
3
+ *
4
+ * Transforms for Prisma-related RUN commands in Dockerfiles.
5
+ * Handles prisma:generate, prisma:seed, and exec prisma commands.
6
+ *
7
+ * Package-manager agnostic: supports npm, yarn, pnpm, and bun.
8
+ */
9
+ import type { InstructionTransformer } from '@harpoon/core';
10
+ /**
11
+ * Transforms package manager prisma commands to include cd to app directory.
12
+ * Handles: prisma:generate, prisma:seed, exec prisma, run build
13
+ * Supports: npm, yarn, pnpm, bun
14
+ */
15
+ export declare const prismaPackageManagerTransformer: InstructionTransformer;
16
+ /**
17
+ * Transforms conditional prisma:seed commands.
18
+ * Handles: if [ "$ENV" = "..." ]; then {npm|yarn|pnpm|bun} prisma:seed; fi
19
+ */
20
+ export declare const prismaConditionalSeedTransformer: InstructionTransformer;
21
+ /**
22
+ * All Prisma transformers for easy registration.
23
+ */
24
+ export declare const prismaTransformers: readonly InstructionTransformer[];
25
+ export default prismaTransformers;
26
+ //# sourceMappingURL=prisma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/transformers/prisma.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAA2C,MAAM,eAAe,CAAC;AA4BrG;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,EAAE,sBA0B7C,CAAC;AAOF;;;GAGG;AACH,eAAO,MAAM,gCAAgC,EAAE,sBA0B9C,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,sBAAsB,EAG9D,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Prisma Dockerfile Transformers
3
+ *
4
+ * Transforms for Prisma-related RUN commands in Dockerfiles.
5
+ * Handles prisma:generate, prisma:seed, and exec prisma commands.
6
+ *
7
+ * Package-manager agnostic: supports npm, yarn, pnpm, and bun.
8
+ */
9
+ import { replaceInstruction } from '@harpoon/core';
10
+ // ============ Helper Functions ============
11
+ /**
12
+ * Package manager prefixes we recognize.
13
+ */
14
+ const PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm', 'bun'];
15
+ /**
16
+ * Check if instruction uses a known package manager.
17
+ */
18
+ function isPackageManagerCommand(inst) {
19
+ const firstArg = inst.args[0] || '';
20
+ return PACKAGE_MANAGERS.some(pm => firstArg.startsWith(pm));
21
+ }
22
+ /**
23
+ * Check if command already has cd to app directory.
24
+ */
25
+ function alreadyHasCd(inst, appName) {
26
+ const originalCmd = inst.original.replace(/^RUN\s+/, '');
27
+ return originalCmd.includes(`cd apps/${appName}`);
28
+ }
29
+ // ============ Transformers ============
30
+ /**
31
+ * Transforms package manager prisma commands to include cd to app directory.
32
+ * Handles: prisma:generate, prisma:seed, exec prisma, run build
33
+ * Supports: npm, yarn, pnpm, bun
34
+ */
35
+ export const prismaPackageManagerTransformer = {
36
+ name: 'prisma-package-manager',
37
+ description: 'Transforms package manager prisma commands for monorepo structure',
38
+ handlesTypes: ['RUN'],
39
+ matches: (inst, ctx) => {
40
+ if (!isPackageManagerCommand(inst))
41
+ return false;
42
+ if (alreadyHasCd(inst, ctx.appName))
43
+ return false;
44
+ const runCmd = inst.args.join(' ');
45
+ return (runCmd.includes('prisma:generate') ||
46
+ runCmd.includes('prisma:seed') ||
47
+ runCmd.includes('exec prisma') ||
48
+ runCmd.includes('run build'));
49
+ },
50
+ transform: (inst, ctx) => {
51
+ const originalCmd = inst.original.replace(/^RUN\s+/, '');
52
+ return replaceInstruction(inst, `RUN cd apps/${ctx.appName} && ${originalCmd}`, Object.freeze([`cd apps/${ctx.appName} && ${originalCmd}`]));
53
+ },
54
+ };
55
+ /**
56
+ * Pattern to match any package manager with prisma:seed.
57
+ */
58
+ const PRISMA_SEED_PATTERN = /(npm|yarn|pnpm|bun)\s+(run\s+)?prisma:seed/;
59
+ /**
60
+ * Transforms conditional prisma:seed commands.
61
+ * Handles: if [ "$ENV" = "..." ]; then {npm|yarn|pnpm|bun} prisma:seed; fi
62
+ */
63
+ export const prismaConditionalSeedTransformer = {
64
+ name: 'prisma-conditional-seed',
65
+ description: 'Transforms conditional prisma:seed commands for monorepo structure',
66
+ handlesTypes: ['RUN'],
67
+ matches: (inst, ctx) => {
68
+ const originalCmd = inst.original.replace(/^RUN\s+/, '');
69
+ return (PRISMA_SEED_PATTERN.test(originalCmd) &&
70
+ !originalCmd.includes(`cd apps/${ctx.appName}`));
71
+ },
72
+ transform: (inst, ctx) => {
73
+ const originalCmd = inst.original.replace(/^RUN\s+/, '');
74
+ // Replace any package manager prisma:seed with cd + command
75
+ const transformedCmd = originalCmd.replace(new RegExp(PRISMA_SEED_PATTERN.source, 'g'), `cd apps/${ctx.appName} && $&`);
76
+ return replaceInstruction(inst, `RUN ${transformedCmd}`, Object.freeze([transformedCmd]));
77
+ },
78
+ };
79
+ // ============ Exports ============
80
+ /**
81
+ * All Prisma transformers for easy registration.
82
+ */
83
+ export const prismaTransformers = Object.freeze([
84
+ prismaPackageManagerTransformer,
85
+ prismaConditionalSeedTransformer,
86
+ ]);
87
+ export default prismaTransformers;
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@docker-harpoon/prisma",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "description": "Prisma binding for Harpoon - database migrations and Dockerfile transformations",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "typecheck": "tsc --noEmit"
19
+ },
20
+ "dependencies": {
21
+ "effect": "^3.19.14"
22
+ },
23
+ "peerDependencies": {
24
+ "@docker-harpoon/core": ">=0.1.0",
25
+ "bun": ">=1.0.0"
26
+ }
27
+ }