Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
87d6cc9
feat(scalar-arrays): lower PSL scalar lists to native array columns (…
SevInf Jun 24, 2026
b88d8eb
feat(scalar-arrays): reject incoherent list authoring constructs (sli…
SevInf Jun 24, 2026
611af14
feat(scalar-arrays): gate scalar-list fields on adapter scalarList ca…
SevInf Jun 24, 2026
59ed908
fix(scalar-arrays): make storage column type maps many-aware (slice 2…
SevInf Jun 24, 2026
7a3d6c4
feat(scalar-arrays): element-non-null CHECK + array-typed defaults fo…
SevInf Jun 24, 2026
11ea914
test(scalar-arrays): end-to-end PSL list milestone + Mongo parity (sl…
SevInf Jun 24, 2026
d0e9ce7
fix(scalar-arrays): supply required createNamespace in capability-gat…
SevInf Jul 1, 2026
4b2a89b
fix(scalar-arrays): address PR review on PSL native scalar lists
SevInf Jul 1, 2026
e251290
chore(scalar-arrays): declare incidental extension-upgrade diff for P…
SevInf Jul 1, 2026
22c4dcb
refactor(scalar-arrays): expose raw expression AST on ResolvedAttribu…
SevInf Jul 1, 2026
3cacbb8
test(scalar-arrays): prove String[]/Int[] read-back via SQL runtime a…
SevInf Jul 2, 2026
633ed88
test(scalar-arrays): typed ORM write->read round-trip over emitted co…
SevInf Jul 2, 2026
2880a10
refactor(scalar-arrays): remove low-value comments per review
SevInf Jul 2, 2026
400f6d7
fix(telemetry-backend): keep extensions/flags on jsonb storage (no DB…
SevInf Jul 2, 2026
31880a9
refactor(scalar-arrays): drop comments describing cross-codebase fiel…
SevInf Jul 2, 2026
7e23046
refactor(scalar-arrays): unify check-constraint mechanisms + drift-ma…
SevInf Jul 1, 2026
3683911
docs(upgrade): record addCheckConstraint payload migration for 0.14->…
SevInf Jul 1, 2026
95f4968
fix(scalar-arrays): parse multi-word element-type casts in array_posi…
SevInf Jul 2, 2026
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
14 changes: 6 additions & 8 deletions apps/telemetry-backend/src/prisma/contract.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions apps/telemetry-backend/src/prisma/contract.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions apps/telemetry-backend/src/prisma/contract.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ model TelemetryEvent {
installationId String
version String
command String
flags String[]
flags Json
runtimeName String
runtimeVersion String
os String
Expand All @@ -15,7 +15,7 @@ model TelemetryEvent {
databaseTarget String?
tsVersion String?
agent String?
extensions String[]
extensions Json

@@index([ingestedAt])
@@map("telemetry_event")
Expand Down
6 changes: 2 additions & 4 deletions apps/telemetry-backend/test/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,8 @@ describe('telemetry POST /events', () => {
const row = await fetchSingleRow();
expect(row.installationId).toBe(maxString);
expect(row.command).toBe(maxString);
expect(row.flags).toHaveLength(64);
expect(row.flags[0]).toBe(maxArrayItem);
expect(row.extensions).toHaveLength(64);
expect(row.extensions[0]).toBe(maxArrayItem);
expect(row.flags).toEqual(manyArrayItems);
expect(row.extensions).toEqual(manyArrayItems);
});

it('rejects strings beyond the configured schema bounds with 400', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,13 @@ export default class M extends Migration<never, End> {
schema: 'public',
table: 'user',
constraint: 'user_kind_check',
column: 'kind',
values: ['admin', 'user'],
payload: { kind: 'valueSet', column: 'kind', values: ['admin', 'user'] },
}),
this.addCheckConstraint({
schema: 'public',
table: 'post',
constraint: 'post_priority_check',
column: 'priority',
values: ['low', 'high', 'urgent'],
payload: { kind: 'valueSet', column: 'priority', values: ['low', 'high', 'urgent'] },
}),
this.addForeignKey({
schema: 'public',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Contract } from '@prisma-next/contract/types';
import type { CodecLookup } from '@prisma-next/framework-components/codec';
import type { CapabilityMatrix } from '@prisma-next/framework-components/components';
import type {
AssembledAuthoringContributions,
ControlMutationDefaults,
Expand Down Expand Up @@ -45,6 +46,7 @@ export interface ContractSourceContext {
readonly codecLookup: CodecLookup;
readonly controlMutationDefaults: ControlMutationDefaults;
readonly resolvedInputs: readonly string[];
readonly capabilities: CapabilityMatrix;
}

/** Lets format-aware tooling avoid file-extension sniffing and opaque loader introspection. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { JsonValue } from '@prisma-next/contract/types';
import { blindCast } from '@prisma-next/utils/casts';
import type { CapabilityMatrix } from '../shared/capabilities';
import { mergeCapabilityMatrices } from '../shared/capabilities';
import type { Codec } from '../shared/codec';
import type { AnyCodecDescriptor } from '../shared/codec-descriptor';
import type { CodecLookup, CodecMeta, CodecRef, CodecRegistry } from '../shared/codec-types';
Expand Down Expand Up @@ -62,6 +64,7 @@ export interface ControlStack<
readonly authoringContributions: AssembledAuthoringContributions;
readonly scalarTypeDescriptors: ReadonlyMap<string, string>;
readonly controlMutationDefaults: ControlMutationDefaults;
readonly capabilities: CapabilityMatrix;
}

export interface CreateControlStackInput<
Expand Down Expand Up @@ -522,5 +525,10 @@ export function createControlStack<TFamilyId extends string, TTargetId extends s
authoringContributions: assembleAuthoringContributions(allDescriptors),
scalarTypeDescriptors,
controlMutationDefaults: assembleControlMutationDefaults(allDescriptors),
capabilities: mergeCapabilityMatrices({}, [
target,
...(adapter ? [adapter] : []),
...orderedExtensionPacks,
]),
};
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export type { CapabilityMatrix } from '../shared/capabilities';
export { mergeCapabilityMatrices } from '../shared/capabilities';
export type {
AdapterDescriptor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import { blindCast } from '@prisma-next/utils/casts';

type CapabilityMatrix = Record<string, Record<string, boolean>>;
export type CapabilityMatrix = Record<string, Record<string, boolean>>;

function isPlainObject(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface ResolvedAttributeArg {
readonly kind: 'positional' | 'named';
readonly name?: string;
readonly value: string;
readonly expression?: ExpressionAst;
readonly span: PslSpan;
}

Expand Down Expand Up @@ -69,10 +70,12 @@ function readResolvedArgList(
const args: ResolvedAttributeArg[] = [];
for (const arg of argList.args()) {
const name = arg.name()?.name();
const expression = arg.value();
args.push({
kind: name !== undefined ? 'named' : 'positional',
...(name !== undefined ? { name } : {}),
value: renderExpression(arg.value()),
value: renderExpression(expression),
...(expression !== undefined ? { expression } : {}),
span: nodePslSpan(arg.syntax, sourceFile),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ class ControlClientImpl implements ControlClient {
codecLookup: stack.codecLookup,
controlMutationDefaults: stack.controlMutationDefaults,
resolvedInputs: contractConfig.source.inputs ?? [],
capabilities: stack.capabilities,
};
const providerResult = await contractConfig.source.load(sourceContext);
if (!providerResult.ok) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export async function executeContractEmit(
codecLookup: stack.codecLookup,
controlMutationDefaults: stack.controlMutationDefaults,
resolvedInputs: contractConfig.source.inputs ?? [],
capabilities: stack.capabilities,
};

startSpan(onProgress, 'resolveSource', 'Resolving contract source...');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ describe('defineConfig', () => {
},
controlMutationDefaults: { defaultFunctionRegistry: new Map(), generatorDescriptors: [] },
resolvedInputs: [],
capabilities: {},
});

expect(config.output).toBe('output/contract.json');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function createMongoTestContext(overrides?: Partial<ContractSourceContext>): Con
generatorDescriptors: [],
},
resolvedInputs: [],
capabilities: {},
...overrides,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const emptyContext: ContractSourceContext = {
generatorDescriptors: [],
},
resolvedInputs: [],
capabilities: {},
};

function minimalMongoContract(overrides?: {
Expand Down
5 changes: 5 additions & 0 deletions packages/2-sql/1-core/schema-ir/src/exports/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type {
SqlAnnotations,
SqlCheckConstraintIRInput,
SqlColumnIRInput,
SqlExpressionCheckIRInput,
SqlForeignKeyIRInput,
SqlIndexIRInput,
SqlReferentialAction,
Expand All @@ -12,16 +13,20 @@ export type {
SqlTypeMetadata,
SqlTypeMetadataRegistry,
SqlUniqueIRInput,
SqlValueSetCheckIRInput,
} from '../types';

export {
PrimaryKey,
SqlCheckConstraintIR,
SqlColumnIR,
SqlExpressionCheckIR,
SqlForeignKeyIR,
SqlIndexIR,
SqlSchemaIR,
SqlSchemaIRNode,
SqlTableIR,
SqlUniqueIR,
SqlValueSetCheckIR,
sqlCheckConstraintIR,
} from '../types';
80 changes: 69 additions & 11 deletions packages/2-sql/1-core/schema-ir/src/ir/sql-check-constraint-ir.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { freezeNode } from '@prisma-next/framework-components/ir';
import { SqlSchemaIRNode } from './sql-schema-ir-node';
import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';

export interface SqlCheckConstraintIRInput {
/**
* Input for a value-set (enum-style `IN (...)`) check constraint. Carries the
* **resolved values** rather than a raw SQL predicate so callers can compare
* value-sets without parsing SQL.
*/
export interface SqlValueSetCheckIRInput {
readonly kind: 'valueSet';
/** Constraint name as stored in the database catalog. */
readonly name: string;
/** Column the check restricts. */
Expand All @@ -11,22 +16,75 @@ export interface SqlCheckConstraintIRInput {
}

/**
* Schema IR node for a table-level check constraint that restricts a
* column to a set of permitted values (an enum-style `IN (...)` check).
* Input for an expression check constraint that carries a canonical SQL
* predicate string (e.g. the scalar-array element-non-null check
* `array_position("tags", NULL) IS NULL`). Compared by strict string equality
* on the canonical predicate.
*/
export interface SqlExpressionCheckIRInput {
readonly kind: 'expression';
/** Constraint name as stored in the database catalog. */
readonly name: string;
/** Canonical SQL predicate emitted verbatim into `CHECK (<expression>)`. */
readonly expression: string;
}

/**
* A table-level check constraint, discriminated on `kind`:
*
* - `valueSet` — an enum-style `column IN (...)` restriction carrying the
* resolved permitted values.
* - `expression` — a canonical SQL predicate (the scalar-array
* element-non-null check) compared by strict string equality.
*/
export type SqlCheckConstraintIRInput = SqlValueSetCheckIRInput | SqlExpressionCheckIRInput;

/**
* Schema IR node for a table-level check constraint.
*
* Carries the **resolved values** rather than a raw SQL predicate so
* callers can compare value-sets without parsing SQL.
* A leaf that earns polymorphic dispatch: value-set and expression checks are
* switched over at the verifier, planner, and introspection sites, so the leaf
* carries its own literal `kind` (per the `IRNodeBase` alphabet contract).
*/
export class SqlCheckConstraintIR extends SqlSchemaIRNode {
export abstract class SqlCheckConstraintIR extends IRNodeBase {
abstract override readonly kind: 'valueSet' | 'expression';
readonly name: string;

protected constructor(name: string) {
super();
this.name = name;
}
}

/** Value-set (`column IN (...)`) check constraint. */
export class SqlValueSetCheckIR extends SqlCheckConstraintIR {
override readonly kind = 'valueSet' as const;
readonly column: string;
readonly permittedValues: readonly string[];

constructor(input: SqlCheckConstraintIRInput) {
super();
this.name = input.name;
constructor(input: Omit<SqlValueSetCheckIRInput, 'kind'>) {
super(input.name);
this.column = input.column;
this.permittedValues = Object.freeze([...input.permittedValues]);
freezeNode(this);
}
}

/** Expression (canonical SQL predicate) check constraint. */
export class SqlExpressionCheckIR extends SqlCheckConstraintIR {
override readonly kind = 'expression' as const;
readonly expression: string;

constructor(input: Omit<SqlExpressionCheckIRInput, 'kind'>) {
super(input.name);
this.expression = input.expression;
freezeNode(this);
}
}

/** Builds the concrete check-constraint IR node for a discriminated input. */
export function sqlCheckConstraintIR(input: SqlCheckConstraintIRInput): SqlCheckConstraintIR {
return input.kind === 'valueSet'
? new SqlValueSetCheckIR(input)
: new SqlExpressionCheckIR(input);
}
10 changes: 6 additions & 4 deletions packages/2-sql/1-core/schema-ir/src/ir/sql-table-ir.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { freezeNode } from '@prisma-next/framework-components/ir';
import { PrimaryKey, type PrimaryKeyInput } from './primary-key';
import { SqlCheckConstraintIR, type SqlCheckConstraintIRInput } from './sql-check-constraint-ir';
import {
SqlCheckConstraintIR,
type SqlCheckConstraintIRInput,
sqlCheckConstraintIR,
} from './sql-check-constraint-ir';
import { type SqlAnnotations, SqlColumnIR, type SqlColumnIRInput } from './sql-column-ir';
import { SqlForeignKeyIR, type SqlForeignKeyIRInput } from './sql-foreign-key-ir';
import { SqlIndexIR, type SqlIndexIRInput } from './sql-index-ir';
Expand Down Expand Up @@ -72,9 +76,7 @@ export class SqlTableIR extends SqlSchemaIRNode {
if (input.annotations !== undefined) this.annotations = input.annotations;
if (input.checks !== undefined && input.checks.length > 0) {
this.checks = Object.freeze(
input.checks.map((c) =>
c instanceof SqlCheckConstraintIR ? c : new SqlCheckConstraintIR(c),
),
input.checks.map((c) => (c instanceof SqlCheckConstraintIR ? c : sqlCheckConstraintIR(c))),
);
}
freezeNode(this);
Expand Down
Loading
Loading