Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/actions/deploy-verify-destroy/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ runs:
if: always() && steps.deploy.outcome != 'skipped'
shell: bash
working-directory: ${{ inputs.working-directory }}
run: bash ../scripts/destroy-guard.sh "${{ inputs.destroy-label }}" module.ts "${{ inputs.stack-name }}"
run: bash "${{ github.workspace }}/scripts/destroy-guard.sh" "${{ inputs.destroy-label }}" module.ts "${{ inputs.stack-name }}"

- name: Sweep leaked per-run projects
# Must run under always(): the CLI creates the per-run project outside
# the Alchemy stack, so destroy doesn't delete it.
if: always()
shell: bash
working-directory: ${{ inputs.working-directory }}
run: bun ../scripts/ci-cleanup.ts ${{ inputs.sweep-prefixes }}
run: bun "${{ github.workspace }}/scripts/ci-cleanup.ts" ${{ inputs.sweep-prefixes }}
2 changes: 1 addition & 1 deletion .github/workflows/e2e-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ jobs:
persist-credentials: false
- uses: ./.github/actions/setup
- run: pnpm install --frozen-lockfile
- run: bun examples/scripts/cold-connect-canary.ts
- run: bun scripts/cold-connect-canary.ts
6 changes: 3 additions & 3 deletions examples/store/modules/storefront/app/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import { mockService } from '@prisma/compose/testing';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it, vi } from 'vitest';
import type Service from '../src/service.ts';
import type Service from '../service.ts';

vi.mock('../src/service.ts', async () => {
const actual = await vi.importActual<{ default: typeof Service }>('../src/service.ts');
vi.mock('../service.ts', async () => {
const actual = await vi.importActual<{ default: typeof Service }>('../service.ts');
return {
default: mockService(actual.default, {
catalog: {
Expand Down
2 changes: 1 addition & 1 deletion examples/store/modules/storefront/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { revalidatePath } from 'next/cache';
import service from '../src/service.ts';
import service from '../service.ts';

// service.load() needs the runtime environment, which doesn't exist at build
// time — so render per request instead of prerendering.
Expand Down
2 changes: 1 addition & 1 deletion examples/store/modules/storefront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"exports": {
".": "./src/service.ts"
".": "./service.ts"
},
"scripts": {
"dev": "next dev",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ export default compute({
catalog: rpc(catalogContract),
orders: rpc(ordersContract),
},
build: nextjs({ module: import.meta.url, appDir: '..' }),
build: nextjs({ module: import.meta.url }),
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* the loopback fake, and the H3 teardown decision (no `close()`) rests on
* bun-test's per-file process isolation.
*
* storefront's build is `nextjs({ appDir })`: the deploy assembler locates the
* storefront's build is `nextjs({ module })`: the deploy assembler locates the
* built `server.js` inside the standalone tree, so this test boots it via the
* same seam (`standaloneServerPath`) rather than re-deriving the path. The
* deploy chain is bootstrap.js -> main.mjs -> server.js; here
Expand All @@ -23,7 +23,7 @@ import type { NextjsBuildAdapter } from '@prisma/compose/nextjs';
import { standaloneServerPath } from '@prisma/compose/nextjs/control';
import { bootstrapService } from '@prisma/compose-prisma-cloud/testing';
import fakeAuthHandler from '@storefront-auth/auth/fake';
import storefrontService from '../src/service.ts';
import storefrontService from '../service.ts';

const PORT = 4310;

Expand Down
6 changes: 3 additions & 3 deletions examples/storefront-auth/modules/storefront/app/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import { mockService } from '@prisma/compose/testing';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it, vi } from 'vitest';
import type Service from '../src/service.ts';
import type Service from '../service.ts';

vi.mock('../src/service.ts', async () => {
const actual = await vi.importActual<{ default: typeof Service }>('../src/service.ts');
vi.mock('../service.ts', async () => {
const actual = await vi.importActual<{ default: typeof Service }>('../service.ts');
return {
default: mockService(actual.default, {
auth: {
Expand Down
2 changes: 1 addition & 1 deletion examples/storefront-auth/modules/storefront/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import service from '../src/service.ts';
import service from '../service.ts';

// service.load() needs the runtime environment, which doesn't exist at build
// time — so render per request instead of prerendering.
Expand Down
2 changes: 1 addition & 1 deletion examples/storefront-auth/modules/storefront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"exports": {
".": "./src/service.ts"
".": "./service.ts"
},
"scripts": {
"dev": "next dev",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { authContract } from '@storefront-auth/auth/contract';
export default compute({
name: 'storefront',
deps: { auth: rpc(authContract) },
// `appDir` is the Next app root; `next build` (output: standalone) is all the
// app does — deploy assembly copies the standalone tree and the static/public
// assets Next omits, and locates server.js itself.
build: nextjs({ module: import.meta.url, appDir: '..' }),
// `next build` (output: standalone) is all the app does — deploy assembly
// copies the standalone tree and the static/public assets Next omits, and
// locates server.js itself. The Next app root defaults to this file's dir.
build: nextjs({ module: import.meta.url }),
});
2 changes: 1 addition & 1 deletion gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ await withConnectionRetry(() => client.dbInit(...), { attempts: 12, delayMs: 500

- Upstream: [FT-5226](https://linear.app/prisma-company/issue/FT-5226/first-connection-to-a-freshly-provisioned-postgres-is-rejected-while)
- Workaround source: [`packages/app-cloud/src/prisma-next-migrate.ts`](packages/app-cloud/src/prisma-next-migrate.ts) (`withConnectionRetry`)
- Removal guard: the CI canary (`examples/scripts/cold-connect-canary.ts`, "Cold-connect canary" E2E job) passes only while the rejection exists — when the platform fixes FT-5226 it goes red, forcing removal of `withConnectionRetry` and itself
- Removal guard: the CI canary (`scripts/cold-connect-canary.ts`, "Cold-connect canary" E2E job) passes only while the rejection exists — when the platform fixes FT-5226 it goes red, forcing removal of `withConnectionRetry` and itself
- Related: [FT-5219](https://linear.app/prisma-company/issue/FT-5219) (idle-close, runtime), [PRO-212](https://linear.app/prisma-company/issue/PRO-212) (nested endpoint DSNs)

---
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"lint:rules:symlinks": "node scripts/sync-agent-rules.mjs --check",
"check:publish-deps": "node scripts/check-publish-deps.mjs",
"test": "turbo run test",
"test:scripts": "node --test scripts/*.test.mjs scripts/*.test.ts examples/scripts/*.test.ts",
"test:scripts": "node --test scripts/*.test.mjs scripts/*.test.ts",
"typecheck": "turbo run typecheck",
"clean": "turbo run clean",
"prepare": "husky && skills add prisma/skills --skill '*' --agent universal claude-code -y && skills add ./skills-contrib --skill '*' --agent universal claude-code -y && node scripts/sync-agent-rules.mjs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ describe('nextjs({ module, appDir })', () => {
});
});

test('appDir defaults to "." — the authoring file\'s own directory is the Next app root', () => {
expect(nextjs({ module: 'file:///app/service.ts' })).toEqual({
extension: '@prisma/compose/nextjs',
type: 'nextjs',
module: 'file:///app/service.ts',
appDir: '.',
entry: 'server.js',
});
});

test('is pure data — calling it twice with the same input yields equal, independent objects', () => {
const a = nextjs({ module: 'file:///app/src/service.ts', appDir: '..' });
const b = nextjs({ module: 'file:///app/src/service.ts', appDir: '..' });
Expand Down
27 changes: 14 additions & 13 deletions packages/0-framework/2-authoring/nextjs/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
/**
* Marks a service as a Next.js app for deployment. `nextjs({ module, appDir })`:
* `module` is the authoring module's `import.meta.url`; `appDir` is your Next
* app's root (the folder with `next.config`, `.next/`, `public/`), resolved
* relative to `dirname(module)` — like an import specifier (ADR-0004). Build it
* with `next build` (`output: "standalone"`); the deploy assembler then does the
* documented standalone deploy — ships the standalone tree and copies in the
* client assets (`.next/static`, `public/`) it omits — so there is no
* build-script step and no path to spell out. Returns plain data; nothing runs
* on import. `extension` + `type` are the control-plane registry key: deploy
* tooling routes assembly through the app's `prisma-compose.config.ts` to this
* package's `/control` descriptor (ADR-0017).
* Marks a service as a Next.js app for deployment. `nextjs({ module })`:
* `module` is the authoring module's `import.meta.url`, and the Next app root
* (the folder with `next.config`, `.next/`, `public/`) defaults to that file's
* own directory. Pass `appDir` — resolved relative to `dirname(module)`, like an
* import specifier (ADR-0004) — only when the authoring file sits elsewhere
* (e.g. a nested `src/`). Build it with `next build` (`output: "standalone"`);
* the deploy assembler then does the documented standalone deploy — ships the
* standalone tree and copies in the client assets (`.next/static`, `public/`) it
* omits — so there is no build-script step and no path to spell out. Returns
* plain data; nothing runs on import. `extension` + `type` are the control-plane
* registry key: deploy tooling routes assembly through the app's
* `prisma-compose.config.ts` to this package's `/control` descriptor (ADR-0017).
*/
import type { BuildAdapter } from '@internal/core';

Expand All @@ -19,11 +20,11 @@ export interface NextjsBuildAdapter extends BuildAdapter {
readonly appDir: string;
}

const nextjsBuild = (opts: { module: string; appDir: string }): NextjsBuildAdapter => ({
const nextjsBuild = (opts: { module: string; appDir?: string }): NextjsBuildAdapter => ({
extension: '@prisma/compose/nextjs',
type: 'nextjs',
module: opts.module,
appDir: opts.appDir,
appDir: opts.appDir ?? '.',
entry: 'server.js',
});

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading