Rebuild on Prisma Next - #3

Merged
SerhiyGreench merged 1 commit into
masterfrom
prisma-8
Aug 28, 2026
Merged

Rebuild on Prisma Next#3
SerhiyGreench merged 1 commit into
masterfrom
prisma-8

Conversation

@SerhiyGreench

@SerhiyGreenchSerhiyGreench commented Aug 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Rebuilds this package on Prisma Next (8.x). Prisma 8 replaces the generated
client with a contract and drops $extends, so the four query extensions
become SqlMiddleware that rewrite a query AST, and everything they used to
read from the generated config maps (SOFT_DELETE_MODELS, AUDIT_CONFIG,
ACCESS_SCOPE_MODELS, …) is now derived from the emitted contract.json.

The Prisma 7 generator is gone with the client that hosted it. emit/ produces
the same decorated @imqueue/rpc classes from the contract, and carries the
three things a contract cannot: validation rules, fields kept off the wire, and
the names of enum members whose database label is not the name (STRING @map("string") had no equivalent, and losing it renamed a published field).

What the ORM cannot do, and what stands in for it:

GoneReplacement
Nested writesrepositoriesFor writes the children itself, in a transaction, taking the foreign key from the row it just created
select on a writeA write returned every column — including a credential secret the caller had not asked for. The façade projects it, reading the row back when the projection names a relation
Atomic update valuesA function passed as one is silently stringified, so a counter has to be x + 1 in the database
groupBy, createMany, skipDuplicatescreateBulk, count, and conflictOn on upsert; the aggregate cases stay one statement rather than a loop

Three decoding bugs this fixes, each of which was silent:

  • pg hands the codec an already-parsed JSON value and the codec parses it
    again. An object survived; a JSON string did not — and a column holding
    "5" came back as the number 5.
  • An enum's array type is numbered when the enum is created, so the driver has
    no parser for it and returns the literal text {EMAIL,SMS}. Every read of
    such a column failed with RUNTIME.DECODE_FAILED.
  • Both are corrected by dataPool, which gives the codec the text it expects
    and looks the enum-array oids up once.

isoDates stays, for a different reason than before. The columns are
timestamptz now, so the instant is no longer ambiguous — but Postgres prints
it as 2026-08-14 09:30:00.123+00, and every boundary downstream refuses that
spelling: the GraphQL DateTime scalar rejects it outright, and so does
z.iso.datetime().

Also new: sqlRunner (a query and a transaction that agree on the
connection, carried in async context so a statement written inside one is in
it), composable sql/raw/join fragments to replace Prisma.sql, and
RowOf<Model> with a Repository<Entity> that lets a call site read a row
without naming its type.

Validated by migrating seven services onto it, all green.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / internal
  • Other: breaking — rebuilt for Prisma Next; the peer moves from
    @prisma/client >=7 to @prisma/orm-postgres >=8.0.0-rc.8 and the
    ./codegen export is removed

Checklist

  • I have read the Contributing guide.
  • Tests added or updated, and the full suite passes locally (npm test) — 86 tests.
  • Docs / doc-blocks updated where relevant.
  • The PR is focused on a single logical change.

Contribution terms (required)

  • I have read and agree to the @imqueue Contribution Terms.
    I grant the project owner the right to license my contribution
    commercially, royalty-free, my contribution stays available under
    GPL-3.0, I keep my copyright, and I understand I will receive no fee for
    it. If I did not agree, I would not be submitting this contribution.

Prisma 8 replaces the generated client with a contract and drops `$extends`,
so the four query extensions become `SqlMiddleware` that rewrite a query AST,
and everything they used to read from the generated config maps is now derived
from the emitted `contract.json` instead.
What the ORM cannot do, and what stands in for it:
* No nested writes. `repositoriesFor` writes the children itself, in a
transaction, taking the foreign key from the row it just created.
* No `select` on a write. A write returned every column, which handed a
caller the credential secret it had not asked for; the façade projects it,
reading the row back when the projection names a relation.
* No atomic update values. A function passed as one is stringified, so a
counter has to be `x + 1` in the database.
* `pg` hands the codec a parsed JSON value and the codec parses it again — a
column holding `"5"` came back as the number 5. `dataPool` gives the codec
the text it expects, and registers the enum-array oids the driver cannot
know, without which every such column fails to decode.
`isoDates` stays, for a different reason than before: the columns are
`timestamptz` now, so the instant is no longer ambiguous, but Postgres prints
it as `2026-08-14 09:30:00.123+00` and every boundary downstream — the GraphQL
DateTime scalar, `z.iso.datetime()` — refuses that spelling.
The Prisma 7 generator is gone. `emit/` produces the same decorated RPC classes
from the contract, and carries what the contract cannot: validation rules,
fields kept off the wire, and the names of enum members whose database label is
not the name.
Migrated seven services against this. 86 tests.
@SerhiyGreench
SerhiyGreench merged commit dc746c4 into masterAug 28, 2026
14 of 15 checks passed
@SerhiyGreench
SerhiyGreench deleted the prisma-8 branch August 28, 2026 08:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 28, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@SerhiyGreench
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Rebuild on Prisma Next - #3

Merged
SerhiyGreench merged 1 commit into
masterfrom
prisma-8
Aug 28, 2026
Merged

Rebuild on Prisma Next#3
SerhiyGreench merged 1 commit into
masterfrom
prisma-8

Conversation

@SerhiyGreench

@SerhiyGreenchSerhiyGreench commented Aug 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Rebuilds this package on Prisma Next (8.x). Prisma 8 replaces the generated
client with a contract and drops $extends, so the four query extensions
become SqlMiddleware that rewrite a query AST, and everything they used to
read from the generated config maps (SOFT_DELETE_MODELS, AUDIT_CONFIG,
ACCESS_SCOPE_MODELS, …) is now derived from the emitted contract.json.

The Prisma 7 generator is gone with the client that hosted it. emit/ produces
the same decorated @imqueue/rpc classes from the contract, and carries the
three things a contract cannot: validation rules, fields kept off the wire, and
the names of enum members whose database label is not the name (STRING @map("string") had no equivalent, and losing it renamed a published field).

What the ORM cannot do, and what stands in for it:

GoneReplacement
Nested writesrepositoriesFor writes the children itself, in a transaction, taking the foreign key from the row it just created
select on a writeA write returned every column — including a credential secret the caller had not asked for. The façade projects it, reading the row back when the projection names a relation
Atomic update valuesA function passed as one is silently stringified, so a counter has to be x + 1 in the database
groupBy, createMany, skipDuplicatescreateBulk, count, and conflictOn on upsert; the aggregate cases stay one statement rather than a loop

Three decoding bugs this fixes, each of which was silent:

  • pg hands the codec an already-parsed JSON value and the codec parses it
    again. An object survived; a JSON string did not — and a column holding
    "5" came back as the number 5.
  • An enum's array type is numbered when the enum is created, so the driver has
    no parser for it and returns the literal text {EMAIL,SMS}. Every read of
    such a column failed with RUNTIME.DECODE_FAILED.
  • Both are corrected by dataPool, which gives the codec the text it expects
    and looks the enum-array oids up once.

isoDates stays, for a different reason than before. The columns are
timestamptz now, so the instant is no longer ambiguous — but Postgres prints
it as 2026-08-14 09:30:00.123+00, and every boundary downstream refuses that
spelling: the GraphQL DateTime scalar rejects it outright, and so does
z.iso.datetime().

Also new: sqlRunner (a query and a transaction that agree on the
connection, carried in async context so a statement written inside one is in
it), composable sql/raw/join fragments to replace Prisma.sql, and
RowOf<Model> with a Repository<Entity> that lets a call site read a row
without naming its type.

Validated by migrating seven services onto it, all green.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / internal
  • Other: breaking — rebuilt for Prisma Next; the peer moves from
    @prisma/client >=7 to @prisma/orm-postgres >=8.0.0-rc.8 and the
    ./codegen export is removed

Checklist

  • I have read the Contributing guide.
  • Tests added or updated, and the full suite passes locally (npm test) — 86 tests.
  • Docs / doc-blocks updated where relevant.
  • The PR is focused on a single logical change.

Contribution terms (required)

  • I have read and agree to the @imqueue Contribution Terms.
    I grant the project owner the right to license my contribution
    commercially, royalty-free, my contribution stays available under
    GPL-3.0, I keep my copyright, and I understand I will receive no fee for
    it. If I did not agree, I would not be submitting this contribution.

Prisma 8 replaces the generated client with a contract and drops `$extends`,
so the four query extensions become `SqlMiddleware` that rewrite a query AST,
and everything they used to read from the generated config maps is now derived
from the emitted `contract.json` instead.
What the ORM cannot do, and what stands in for it:
* No nested writes. `repositoriesFor` writes the children itself, in a
transaction, taking the foreign key from the row it just created.
* No `select` on a write. A write returned every column, which handed a
caller the credential secret it had not asked for; the façade projects it,
reading the row back when the projection names a relation.
* No atomic update values. A function passed as one is stringified, so a
counter has to be `x + 1` in the database.
* `pg` hands the codec a parsed JSON value and the codec parses it again — a
column holding `"5"` came back as the number 5. `dataPool` gives the codec
the text it expects, and registers the enum-array oids the driver cannot
know, without which every such column fails to decode.
`isoDates` stays, for a different reason than before: the columns are
`timestamptz` now, so the instant is no longer ambiguous, but Postgres prints
it as `2026-08-14 09:30:00.123+00` and every boundary downstream — the GraphQL
DateTime scalar, `z.iso.datetime()` — refuses that spelling.
The Prisma 7 generator is gone. `emit/` produces the same decorated RPC classes
from the contract, and carries what the contract cannot: validation rules,
fields kept off the wire, and the names of enum members whose database label is
not the name.
Migrated seven services against this. 86 tests.
@SerhiyGreench
SerhiyGreench merged commit dc746c4 into masterAug 28, 2026
14 of 15 checks passed
@SerhiyGreench
SerhiyGreench deleted the prisma-8 branch August 28, 2026 08:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 28, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@SerhiyGreench
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Rebuild on Prisma Next - #3

Merged
SerhiyGreench merged 1 commit into
masterfrom
prisma-8
Aug 28, 2026
Merged

Rebuild on Prisma Next#3
SerhiyGreench merged 1 commit into
masterfrom
prisma-8

Conversation

@SerhiyGreench

@SerhiyGreenchSerhiyGreench commented Aug 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Rebuilds this package on Prisma Next (8.x). Prisma 8 replaces the generated
client with a contract and drops $extends, so the four query extensions
become SqlMiddleware that rewrite a query AST, and everything they used to
read from the generated config maps (SOFT_DELETE_MODELS, AUDIT_CONFIG,
ACCESS_SCOPE_MODELS, …) is now derived from the emitted contract.json.

The Prisma 7 generator is gone with the client that hosted it. emit/ produces
the same decorated @imqueue/rpc classes from the contract, and carries the
three things a contract cannot: validation rules, fields kept off the wire, and
the names of enum members whose database label is not the name (STRING @map("string") had no equivalent, and losing it renamed a published field).

What the ORM cannot do, and what stands in for it:

GoneReplacement
Nested writesrepositoriesFor writes the children itself, in a transaction, taking the foreign key from the row it just created
select on a writeA write returned every column — including a credential secret the caller had not asked for. The façade projects it, reading the row back when the projection names a relation
Atomic update valuesA function passed as one is silently stringified, so a counter has to be x + 1 in the database
groupBy, createMany, skipDuplicatescreateBulk, count, and conflictOn on upsert; the aggregate cases stay one statement rather than a loop

Three decoding bugs this fixes, each of which was silent:

  • pg hands the codec an already-parsed JSON value and the codec parses it
    again. An object survived; a JSON string did not — and a column holding
    "5" came back as the number 5.
  • An enum's array type is numbered when the enum is created, so the driver has
    no parser for it and returns the literal text {EMAIL,SMS}. Every read of
    such a column failed with RUNTIME.DECODE_FAILED.
  • Both are corrected by dataPool, which gives the codec the text it expects
    and looks the enum-array oids up once.

isoDates stays, for a different reason than before. The columns are
timestamptz now, so the instant is no longer ambiguous — but Postgres prints
it as 2026-08-14 09:30:00.123+00, and every boundary downstream refuses that
spelling: the GraphQL DateTime scalar rejects it outright, and so does
z.iso.datetime().

Also new: sqlRunner (a query and a transaction that agree on the
connection, carried in async context so a statement written inside one is in
it), composable sql/raw/join fragments to replace Prisma.sql, and
RowOf<Model> with a Repository<Entity> that lets a call site read a row
without naming its type.

Validated by migrating seven services onto it, all green.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / internal
  • Other: breaking — rebuilt for Prisma Next; the peer moves from
    @prisma/client >=7 to @prisma/orm-postgres >=8.0.0-rc.8 and the
    ./codegen export is removed

Checklist

  • I have read the Contributing guide.
  • Tests added or updated, and the full suite passes locally (npm test) — 86 tests.
  • Docs / doc-blocks updated where relevant.
  • The PR is focused on a single logical change.

Contribution terms (required)

  • I have read and agree to the @imqueue Contribution Terms.
    I grant the project owner the right to license my contribution
    commercially, royalty-free, my contribution stays available under
    GPL-3.0, I keep my copyright, and I understand I will receive no fee for
    it. If I did not agree, I would not be submitting this contribution.

Prisma 8 replaces the generated client with a contract and drops `$extends`,
so the four query extensions become `SqlMiddleware` that rewrite a query AST,
and everything they used to read from the generated config maps is now derived
from the emitted `contract.json` instead.
What the ORM cannot do, and what stands in for it:
* No nested writes. `repositoriesFor` writes the children itself, in a
transaction, taking the foreign key from the row it just created.
* No `select` on a write. A write returned every column, which handed a
caller the credential secret it had not asked for; the façade projects it,
reading the row back when the projection names a relation.
* No atomic update values. A function passed as one is stringified, so a
counter has to be `x + 1` in the database.
* `pg` hands the codec a parsed JSON value and the codec parses it again — a
column holding `"5"` came back as the number 5. `dataPool` gives the codec
the text it expects, and registers the enum-array oids the driver cannot
know, without which every such column fails to decode.
`isoDates` stays, for a different reason than before: the columns are
`timestamptz` now, so the instant is no longer ambiguous, but Postgres prints
it as `2026-08-14 09:30:00.123+00` and every boundary downstream — the GraphQL
DateTime scalar, `z.iso.datetime()` — refuses that spelling.
The Prisma 7 generator is gone. `emit/` produces the same decorated RPC classes
from the contract, and carries what the contract cannot: validation rules,
fields kept off the wire, and the names of enum members whose database label is
not the name.
Migrated seven services against this. 86 tests.
@SerhiyGreench
SerhiyGreench merged commit dc746c4 into masterAug 28, 2026
14 of 15 checks passed
@SerhiyGreench
SerhiyGreench deleted the prisma-8 branch August 28, 2026 08:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 28, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@SerhiyGreench
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Rebuild on Prisma Next - #3

Merged
SerhiyGreench merged 1 commit into
masterfrom
prisma-8
Aug 28, 2026
Merged

Rebuild on Prisma Next#3
SerhiyGreench merged 1 commit into
masterfrom
prisma-8

Conversation

@SerhiyGreench

@SerhiyGreenchSerhiyGreench commented Aug 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Rebuilds this package on Prisma Next (8.x). Prisma 8 replaces the generated
client with a contract and drops $extends, so the four query extensions
become SqlMiddleware that rewrite a query AST, and everything they used to
read from the generated config maps (SOFT_DELETE_MODELS, AUDIT_CONFIG,
ACCESS_SCOPE_MODELS, …) is now derived from the emitted contract.json.

The Prisma 7 generator is gone with the client that hosted it. emit/ produces
the same decorated @imqueue/rpc classes from the contract, and carries the
three things a contract cannot: validation rules, fields kept off the wire, and
the names of enum members whose database label is not the name (STRING @map("string") had no equivalent, and losing it renamed a published field).

What the ORM cannot do, and what stands in for it:

GoneReplacement
Nested writesrepositoriesFor writes the children itself, in a transaction, taking the foreign key from the row it just created
select on a writeA write returned every column — including a credential secret the caller had not asked for. The façade projects it, reading the row back when the projection names a relation
Atomic update valuesA function passed as one is silently stringified, so a counter has to be x + 1 in the database
groupBy, createMany, skipDuplicatescreateBulk, count, and conflictOn on upsert; the aggregate cases stay one statement rather than a loop

Three decoding bugs this fixes, each of which was silent:

  • pg hands the codec an already-parsed JSON value and the codec parses it
    again. An object survived; a JSON string did not — and a column holding
    "5" came back as the number 5.
  • An enum's array type is numbered when the enum is created, so the driver has
    no parser for it and returns the literal text {EMAIL,SMS}. Every read of
    such a column failed with RUNTIME.DECODE_FAILED.
  • Both are corrected by dataPool, which gives the codec the text it expects
    and looks the enum-array oids up once.

isoDates stays, for a different reason than before. The columns are
timestamptz now, so the instant is no longer ambiguous — but Postgres prints
it as 2026-08-14 09:30:00.123+00, and every boundary downstream refuses that
spelling: the GraphQL DateTime scalar rejects it outright, and so does
z.iso.datetime().

Also new: sqlRunner (a query and a transaction that agree on the
connection, carried in async context so a statement written inside one is in
it), composable sql/raw/join fragments to replace Prisma.sql, and
RowOf<Model> with a Repository<Entity> that lets a call site read a row
without naming its type.

Validated by migrating seven services onto it, all green.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / internal
  • Other: breaking — rebuilt for Prisma Next; the peer moves from
    @prisma/client >=7 to @prisma/orm-postgres >=8.0.0-rc.8 and the
    ./codegen export is removed

Checklist

  • I have read the Contributing guide.
  • Tests added or updated, and the full suite passes locally (npm test) — 86 tests.
  • Docs / doc-blocks updated where relevant.
  • The PR is focused on a single logical change.

Contribution terms (required)

  • I have read and agree to the @imqueue Contribution Terms.
    I grant the project owner the right to license my contribution
    commercially, royalty-free, my contribution stays available under
    GPL-3.0, I keep my copyright, and I understand I will receive no fee for
    it. If I did not agree, I would not be submitting this contribution.

Prisma 8 replaces the generated client with a contract and drops `$extends`,
so the four query extensions become `SqlMiddleware` that rewrite a query AST,
and everything they used to read from the generated config maps is now derived
from the emitted `contract.json` instead.
What the ORM cannot do, and what stands in for it:
* No nested writes. `repositoriesFor` writes the children itself, in a
transaction, taking the foreign key from the row it just created.
* No `select` on a write. A write returned every column, which handed a
caller the credential secret it had not asked for; the façade projects it,
reading the row back when the projection names a relation.
* No atomic update values. A function passed as one is stringified, so a
counter has to be `x + 1` in the database.
* `pg` hands the codec a parsed JSON value and the codec parses it again — a
column holding `"5"` came back as the number 5. `dataPool` gives the codec
the text it expects, and registers the enum-array oids the driver cannot
know, without which every such column fails to decode.
`isoDates` stays, for a different reason than before: the columns are
`timestamptz` now, so the instant is no longer ambiguous, but Postgres prints
it as `2026-08-14 09:30:00.123+00` and every boundary downstream — the GraphQL
DateTime scalar, `z.iso.datetime()` — refuses that spelling.
The Prisma 7 generator is gone. `emit/` produces the same decorated RPC classes
from the contract, and carries what the contract cannot: validation rules,
fields kept off the wire, and the names of enum members whose database label is
not the name.
Migrated seven services against this. 86 tests.
@SerhiyGreench
SerhiyGreench merged commit dc746c4 into masterAug 28, 2026
14 of 15 checks passed
@SerhiyGreench
SerhiyGreench deleted the prisma-8 branch August 28, 2026 08:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 28, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@SerhiyGreench
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Rebuild on Prisma Next - #3

Merged
SerhiyGreench merged 1 commit into
masterfrom
prisma-8
Aug 28, 2026
Merged

Rebuild on Prisma Next#3
SerhiyGreench merged 1 commit into
masterfrom
prisma-8

Conversation

@SerhiyGreench

@SerhiyGreenchSerhiyGreench commented Aug 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Rebuilds this package on Prisma Next (8.x). Prisma 8 replaces the generated
client with a contract and drops $extends, so the four query extensions
become SqlMiddleware that rewrite a query AST, and everything they used to
read from the generated config maps (SOFT_DELETE_MODELS, AUDIT_CONFIG,
ACCESS_SCOPE_MODELS, …) is now derived from the emitted contract.json.

The Prisma 7 generator is gone with the client that hosted it. emit/ produces
the same decorated @imqueue/rpc classes from the contract, and carries the
three things a contract cannot: validation rules, fields kept off the wire, and
the names of enum members whose database label is not the name (STRING @map("string") had no equivalent, and losing it renamed a published field).

What the ORM cannot do, and what stands in for it:

GoneReplacement
Nested writesrepositoriesFor writes the children itself, in a transaction, taking the foreign key from the row it just created
select on a writeA write returned every column — including a credential secret the caller had not asked for. The façade projects it, reading the row back when the projection names a relation
Atomic update valuesA function passed as one is silently stringified, so a counter has to be x + 1 in the database
groupBy, createMany, skipDuplicatescreateBulk, count, and conflictOn on upsert; the aggregate cases stay one statement rather than a loop

Three decoding bugs this fixes, each of which was silent:

  • pg hands the codec an already-parsed JSON value and the codec parses it
    again. An object survived; a JSON string did not — and a column holding
    "5" came back as the number 5.
  • An enum's array type is numbered when the enum is created, so the driver has
    no parser for it and returns the literal text {EMAIL,SMS}. Every read of
    such a column failed with RUNTIME.DECODE_FAILED.
  • Both are corrected by dataPool, which gives the codec the text it expects
    and looks the enum-array oids up once.

isoDates stays, for a different reason than before. The columns are
timestamptz now, so the instant is no longer ambiguous — but Postgres prints
it as 2026-08-14 09:30:00.123+00, and every boundary downstream refuses that
spelling: the GraphQL DateTime scalar rejects it outright, and so does
z.iso.datetime().

Also new: sqlRunner (a query and a transaction that agree on the
connection, carried in async context so a statement written inside one is in
it), composable sql/raw/join fragments to replace Prisma.sql, and
RowOf<Model> with a Repository<Entity> that lets a call site read a row
without naming its type.

Validated by migrating seven services onto it, all green.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / internal
  • Other: breaking — rebuilt for Prisma Next; the peer moves from
    @prisma/client >=7 to @prisma/orm-postgres >=8.0.0-rc.8 and the
    ./codegen export is removed

Checklist

  • I have read the Contributing guide.
  • Tests added or updated, and the full suite passes locally (npm test) — 86 tests.
  • Docs / doc-blocks updated where relevant.
  • The PR is focused on a single logical change.

Contribution terms (required)

  • I have read and agree to the @imqueue Contribution Terms.
    I grant the project owner the right to license my contribution
    commercially, royalty-free, my contribution stays available under
    GPL-3.0, I keep my copyright, and I understand I will receive no fee for
    it. If I did not agree, I would not be submitting this contribution.

Prisma 8 replaces the generated client with a contract and drops `$extends`,
so the four query extensions become `SqlMiddleware` that rewrite a query AST,
and everything they used to read from the generated config maps is now derived
from the emitted `contract.json` instead.
What the ORM cannot do, and what stands in for it:
* No nested writes. `repositoriesFor` writes the children itself, in a
transaction, taking the foreign key from the row it just created.
* No `select` on a write. A write returned every column, which handed a
caller the credential secret it had not asked for; the façade projects it,
reading the row back when the projection names a relation.
* No atomic update values. A function passed as one is stringified, so a
counter has to be `x + 1` in the database.
* `pg` hands the codec a parsed JSON value and the codec parses it again — a
column holding `"5"` came back as the number 5. `dataPool` gives the codec
the text it expects, and registers the enum-array oids the driver cannot
know, without which every such column fails to decode.
`isoDates` stays, for a different reason than before: the columns are
`timestamptz` now, so the instant is no longer ambiguous, but Postgres prints
it as `2026-08-14 09:30:00.123+00` and every boundary downstream — the GraphQL
DateTime scalar, `z.iso.datetime()` — refuses that spelling.
The Prisma 7 generator is gone. `emit/` produces the same decorated RPC classes
from the contract, and carries what the contract cannot: validation rules,
fields kept off the wire, and the names of enum members whose database label is
not the name.
Migrated seven services against this. 86 tests.
@SerhiyGreench
SerhiyGreench merged commit dc746c4 into masterAug 28, 2026
14 of 15 checks passed
@SerhiyGreench
SerhiyGreench deleted the prisma-8 branch August 28, 2026 08:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 28, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@SerhiyGreench
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Rebuild on Prisma Next - #3

Merged
SerhiyGreench merged 1 commit into
masterfrom
prisma-8
Aug 28, 2026
Merged

Rebuild on Prisma Next#3
SerhiyGreench merged 1 commit into
masterfrom
prisma-8

Conversation

@SerhiyGreench

@SerhiyGreenchSerhiyGreench commented Aug 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Rebuilds this package on Prisma Next (8.x). Prisma 8 replaces the generated
client with a contract and drops $extends, so the four query extensions
become SqlMiddleware that rewrite a query AST, and everything they used to
read from the generated config maps (SOFT_DELETE_MODELS, AUDIT_CONFIG,
ACCESS_SCOPE_MODELS, …) is now derived from the emitted contract.json.

The Prisma 7 generator is gone with the client that hosted it. emit/ produces
the same decorated @imqueue/rpc classes from the contract, and carries the
three things a contract cannot: validation rules, fields kept off the wire, and
the names of enum members whose database label is not the name (STRING @map("string") had no equivalent, and losing it renamed a published field).

What the ORM cannot do, and what stands in for it:

GoneReplacement
Nested writesrepositoriesFor writes the children itself, in a transaction, taking the foreign key from the row it just created
select on a writeA write returned every column — including a credential secret the caller had not asked for. The façade projects it, reading the row back when the projection names a relation
Atomic update valuesA function passed as one is silently stringified, so a counter has to be x + 1 in the database
groupBy, createMany, skipDuplicatescreateBulk, count, and conflictOn on upsert; the aggregate cases stay one statement rather than a loop

Three decoding bugs this fixes, each of which was silent:

  • pg hands the codec an already-parsed JSON value and the codec parses it
    again. An object survived; a JSON string did not — and a column holding
    "5" came back as the number 5.
  • An enum's array type is numbered when the enum is created, so the driver has
    no parser for it and returns the literal text {EMAIL,SMS}. Every read of
    such a column failed with RUNTIME.DECODE_FAILED.
  • Both are corrected by dataPool, which gives the codec the text it expects
    and looks the enum-array oids up once.

isoDates stays, for a different reason than before. The columns are
timestamptz now, so the instant is no longer ambiguous — but Postgres prints
it as 2026-08-14 09:30:00.123+00, and every boundary downstream refuses that
spelling: the GraphQL DateTime scalar rejects it outright, and so does
z.iso.datetime().

Also new: sqlRunner (a query and a transaction that agree on the
connection, carried in async context so a statement written inside one is in
it), composable sql/raw/join fragments to replace Prisma.sql, and
RowOf<Model> with a Repository<Entity> that lets a call site read a row
without naming its type.

Validated by migrating seven services onto it, all green.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / internal
  • Other: breaking — rebuilt for Prisma Next; the peer moves from
    @prisma/client >=7 to @prisma/orm-postgres >=8.0.0-rc.8 and the
    ./codegen export is removed

Checklist

  • I have read the Contributing guide.
  • Tests added or updated, and the full suite passes locally (npm test) — 86 tests.
  • Docs / doc-blocks updated where relevant.
  • The PR is focused on a single logical change.

Contribution terms (required)

  • I have read and agree to the @imqueue Contribution Terms.
    I grant the project owner the right to license my contribution
    commercially, royalty-free, my contribution stays available under
    GPL-3.0, I keep my copyright, and I understand I will receive no fee for
    it. If I did not agree, I would not be submitting this contribution.

Prisma 8 replaces the generated client with a contract and drops `$extends`,
so the four query extensions become `SqlMiddleware` that rewrite a query AST,
and everything they used to read from the generated config maps is now derived
from the emitted `contract.json` instead.
What the ORM cannot do, and what stands in for it:
* No nested writes. `repositoriesFor` writes the children itself, in a
transaction, taking the foreign key from the row it just created.
* No `select` on a write. A write returned every column, which handed a
caller the credential secret it had not asked for; the façade projects it,
reading the row back when the projection names a relation.
* No atomic update values. A function passed as one is stringified, so a
counter has to be `x + 1` in the database.
* `pg` hands the codec a parsed JSON value and the codec parses it again — a
column holding `"5"` came back as the number 5. `dataPool` gives the codec
the text it expects, and registers the enum-array oids the driver cannot
know, without which every such column fails to decode.
`isoDates` stays, for a different reason than before: the columns are
`timestamptz` now, so the instant is no longer ambiguous, but Postgres prints
it as `2026-08-14 09:30:00.123+00` and every boundary downstream — the GraphQL
DateTime scalar, `z.iso.datetime()` — refuses that spelling.
The Prisma 7 generator is gone. `emit/` produces the same decorated RPC classes
from the contract, and carries what the contract cannot: validation rules,
fields kept off the wire, and the names of enum members whose database label is
not the name.
Migrated seven services against this. 86 tests.
@SerhiyGreench
SerhiyGreench merged commit dc746c4 into masterAug 28, 2026
14 of 15 checks passed
@SerhiyGreench
SerhiyGreench deleted the prisma-8 branch August 28, 2026 08:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 28, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@SerhiyGreench
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Rebuild on Prisma Next - #3

Merged
SerhiyGreench merged 1 commit into
masterfrom
prisma-8
Aug 28, 2026
Merged

Rebuild on Prisma Next#3
SerhiyGreench merged 1 commit into
masterfrom
prisma-8

Conversation

@SerhiyGreench

@SerhiyGreenchSerhiyGreench commented Aug 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Rebuilds this package on Prisma Next (8.x). Prisma 8 replaces the generated
client with a contract and drops $extends, so the four query extensions
become SqlMiddleware that rewrite a query AST, and everything they used to
read from the generated config maps (SOFT_DELETE_MODELS, AUDIT_CONFIG,
ACCESS_SCOPE_MODELS, …) is now derived from the emitted contract.json.

The Prisma 7 generator is gone with the client that hosted it. emit/ produces
the same decorated @imqueue/rpc classes from the contract, and carries the
three things a contract cannot: validation rules, fields kept off the wire, and
the names of enum members whose database label is not the name (STRING @map("string") had no equivalent, and losing it renamed a published field).

What the ORM cannot do, and what stands in for it:

GoneReplacement
Nested writesrepositoriesFor writes the children itself, in a transaction, taking the foreign key from the row it just created
select on a writeA write returned every column — including a credential secret the caller had not asked for. The façade projects it, reading the row back when the projection names a relation
Atomic update valuesA function passed as one is silently stringified, so a counter has to be x + 1 in the database
groupBy, createMany, skipDuplicatescreateBulk, count, and conflictOn on upsert; the aggregate cases stay one statement rather than a loop

Three decoding bugs this fixes, each of which was silent:

  • pg hands the codec an already-parsed JSON value and the codec parses it
    again. An object survived; a JSON string did not — and a column holding
    "5" came back as the number 5.
  • An enum's array type is numbered when the enum is created, so the driver has
    no parser for it and returns the literal text {EMAIL,SMS}. Every read of
    such a column failed with RUNTIME.DECODE_FAILED.
  • Both are corrected by dataPool, which gives the codec the text it expects
    and looks the enum-array oids up once.

isoDates stays, for a different reason than before. The columns are
timestamptz now, so the instant is no longer ambiguous — but Postgres prints
it as 2026-08-14 09:30:00.123+00, and every boundary downstream refuses that
spelling: the GraphQL DateTime scalar rejects it outright, and so does
z.iso.datetime().

Also new: sqlRunner (a query and a transaction that agree on the
connection, carried in async context so a statement written inside one is in
it), composable sql/raw/join fragments to replace Prisma.sql, and
RowOf<Model> with a Repository<Entity> that lets a call site read a row
without naming its type.

Validated by migrating seven services onto it, all green.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / internal
  • Other: breaking — rebuilt for Prisma Next; the peer moves from
    @prisma/client >=7 to @prisma/orm-postgres >=8.0.0-rc.8 and the
    ./codegen export is removed

Checklist

  • I have read the Contributing guide.
  • Tests added or updated, and the full suite passes locally (npm test) — 86 tests.
  • Docs / doc-blocks updated where relevant.
  • The PR is focused on a single logical change.

Contribution terms (required)

  • I have read and agree to the @imqueue Contribution Terms.
    I grant the project owner the right to license my contribution
    commercially, royalty-free, my contribution stays available under
    GPL-3.0, I keep my copyright, and I understand I will receive no fee for
    it. If I did not agree, I would not be submitting this contribution.

Prisma 8 replaces the generated client with a contract and drops `$extends`,
so the four query extensions become `SqlMiddleware` that rewrite a query AST,
and everything they used to read from the generated config maps is now derived
from the emitted `contract.json` instead.
What the ORM cannot do, and what stands in for it:
* No nested writes. `repositoriesFor` writes the children itself, in a
transaction, taking the foreign key from the row it just created.
* No `select` on a write. A write returned every column, which handed a
caller the credential secret it had not asked for; the façade projects it,
reading the row back when the projection names a relation.
* No atomic update values. A function passed as one is stringified, so a
counter has to be `x + 1` in the database.
* `pg` hands the codec a parsed JSON value and the codec parses it again — a
column holding `"5"` came back as the number 5. `dataPool` gives the codec
the text it expects, and registers the enum-array oids the driver cannot
know, without which every such column fails to decode.
`isoDates` stays, for a different reason than before: the columns are
`timestamptz` now, so the instant is no longer ambiguous, but Postgres prints
it as `2026-08-14 09:30:00.123+00` and every boundary downstream — the GraphQL
DateTime scalar, `z.iso.datetime()` — refuses that spelling.
The Prisma 7 generator is gone. `emit/` produces the same decorated RPC classes
from the contract, and carries what the contract cannot: validation rules,
fields kept off the wire, and the names of enum members whose database label is
not the name.
Migrated seven services against this. 86 tests.
@SerhiyGreench
SerhiyGreench merged commit dc746c4 into masterAug 28, 2026
14 of 15 checks passed
@SerhiyGreench
SerhiyGreench deleted the prisma-8 branch August 28, 2026 08:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 28, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@SerhiyGreench
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Rebuild on Prisma Next - #3

Merged
SerhiyGreench merged 1 commit into
masterfrom
prisma-8
Aug 28, 2026
Merged

Rebuild on Prisma Next#3
SerhiyGreench merged 1 commit into
masterfrom
prisma-8

Conversation

@SerhiyGreench

@SerhiyGreenchSerhiyGreench commented Aug 28, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Rebuilds this package on Prisma Next (8.x). Prisma 8 replaces the generated
client with a contract and drops $extends, so the four query extensions
become SqlMiddleware that rewrite a query AST, and everything they used to
read from the generated config maps (SOFT_DELETE_MODELS, AUDIT_CONFIG,
ACCESS_SCOPE_MODELS, …) is now derived from the emitted contract.json.

The Prisma 7 generator is gone with the client that hosted it. emit/ produces
the same decorated @imqueue/rpc classes from the contract, and carries the
three things a contract cannot: validation rules, fields kept off the wire, and
the names of enum members whose database label is not the name (STRING @map("string") had no equivalent, and losing it renamed a published field).

What the ORM cannot do, and what stands in for it:

GoneReplacement
Nested writesrepositoriesFor writes the children itself, in a transaction, taking the foreign key from the row it just created
select on a writeA write returned every column — including a credential secret the caller had not asked for. The façade projects it, reading the row back when the projection names a relation
Atomic update valuesA function passed as one is silently stringified, so a counter has to be x + 1 in the database
groupBy, createMany, skipDuplicatescreateBulk, count, and conflictOn on upsert; the aggregate cases stay one statement rather than a loop

Three decoding bugs this fixes, each of which was silent:

  • pg hands the codec an already-parsed JSON value and the codec parses it
    again. An object survived; a JSON string did not — and a column holding
    "5" came back as the number 5.
  • An enum's array type is numbered when the enum is created, so the driver has
    no parser for it and returns the literal text {EMAIL,SMS}. Every read of
    such a column failed with RUNTIME.DECODE_FAILED.
  • Both are corrected by dataPool, which gives the codec the text it expects
    and looks the enum-array oids up once.

isoDates stays, for a different reason than before. The columns are
timestamptz now, so the instant is no longer ambiguous — but Postgres prints
it as 2026-08-14 09:30:00.123+00, and every boundary downstream refuses that
spelling: the GraphQL DateTime scalar rejects it outright, and so does
z.iso.datetime().

Also new: sqlRunner (a query and a transaction that agree on the
connection, carried in async context so a statement written inside one is in
it), composable sql/raw/join fragments to replace Prisma.sql, and
RowOf<Model> with a Repository<Entity> that lets a call site read a row
without naming its type.

Validated by migrating seven services onto it, all green.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / internal
  • Other: breaking — rebuilt for Prisma Next; the peer moves from
    @prisma/client >=7 to @prisma/orm-postgres >=8.0.0-rc.8 and the
    ./codegen export is removed

Checklist

  • I have read the Contributing guide.
  • Tests added or updated, and the full suite passes locally (npm test) — 86 tests.
  • Docs / doc-blocks updated where relevant.
  • The PR is focused on a single logical change.

Contribution terms (required)

  • I have read and agree to the @imqueue Contribution Terms.
    I grant the project owner the right to license my contribution
    commercially, royalty-free, my contribution stays available under
    GPL-3.0, I keep my copyright, and I understand I will receive no fee for
    it. If I did not agree, I would not be submitting this contribution.

Prisma 8 replaces the generated client with a contract and drops `$extends`,
so the four query extensions become `SqlMiddleware` that rewrite a query AST,
and everything they used to read from the generated config maps is now derived
from the emitted `contract.json` instead.
What the ORM cannot do, and what stands in for it:
* No nested writes. `repositoriesFor` writes the children itself, in a
transaction, taking the foreign key from the row it just created.
* No `select` on a write. A write returned every column, which handed a
caller the credential secret it had not asked for; the façade projects it,
reading the row back when the projection names a relation.
* No atomic update values. A function passed as one is stringified, so a
counter has to be `x + 1` in the database.
* `pg` hands the codec a parsed JSON value and the codec parses it again — a
column holding `"5"` came back as the number 5. `dataPool` gives the codec
the text it expects, and registers the enum-array oids the driver cannot
know, without which every such column fails to decode.
`isoDates` stays, for a different reason than before: the columns are
`timestamptz` now, so the instant is no longer ambiguous, but Postgres prints
it as `2026-08-14 09:30:00.123+00` and every boundary downstream — the GraphQL
DateTime scalar, `z.iso.datetime()` — refuses that spelling.
The Prisma 7 generator is gone. `emit/` produces the same decorated RPC classes
from the contract, and carries what the contract cannot: validation rules,
fields kept off the wire, and the names of enum members whose database label is
not the name.
Migrated seven services against this. 86 tests.
@SerhiyGreench
SerhiyGreench merged commit dc746c4 into masterAug 28, 2026
14 of 15 checks passed
@SerhiyGreench
SerhiyGreench deleted the prisma-8 branch August 28, 2026 08:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 28, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@SerhiyGreench