@prisma-next/sql-contract-psl 0.12.0-dev.30 → 0.12.0-dev.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-o0m3OC4P.mjs → interpreter-B75uZQde.mjs} +74 -2
- package/dist/interpreter-B75uZQde.mjs.map +1 -0
- package/dist/provider.d.mts +2 -0
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +3 -2
- package/dist/provider.mjs.map +1 -1
- package/package.json +12 -12
- package/src/interpreter.ts +26 -0
- package/src/provider.ts +6 -1
- package/src/psl-attribute-parsing.ts +66 -0
- package/dist/interpreter-o0m3OC4P.mjs.map +0 -1
package/dist/provider.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.mjs","names":[],"sources":["../src/provider.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ContractConfig } from '@prisma-next/config/config-types';\nimport type { CodecLookup } from '@prisma-next/framework-components/codec';\nimport type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';\nimport { parsePslDocument } from '@prisma-next/psl-parser';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { basename, extname } from 'pathe';\nimport { interpretPslDocumentToSqlContract } from './interpreter';\nimport type { ColumnDescriptor } from './psl-column-resolution';\n\nexport interface PrismaContractOptions {\n readonly output?: string;\n readonly target: TargetPackRef<'sql', string>;\n readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql', string>[];\n}\n\n/**\n * Derives the emit output path from the schema input path so artefacts land\n * colocated with the source (e.g. `src/contract/schema.prisma` →\n * `src/contract/contract.json`). The provider owns this because it is the\n * only layer that knows the input path; the upstream `normalizeContractConfig`\n * default is a last-resort fallback for providers that don't carry one.\n */\nfunction defaultOutputFromSchemaPath(schemaPath: string): string {\n const ext = extname(schemaPath);\n if (ext.length === 0) return `${schemaPath}.json`;\n const base = schemaPath.slice(0, -ext.length);\n // PSL schemas commonly use `schema.prisma`; the emitted JSON is called\n // `contract.json` to mirror the rest of the toolchain, not `schema.json`.\n // Match only the exact basename `schema` so files like `my-schema.prisma`\n // are not silently rewritten to `my-contract.json`.\n if (basename(base) === 'schema') {\n return `${base.slice(0, -'schema'.length)}contract.json`;\n }\n return `${base}.json`;\n}\n\nfunction buildColumnDescriptorMap(\n scalarTypeDescriptors: ReadonlyMap<string, string>,\n codecLookup: CodecLookup,\n): ReadonlyMap<string, ColumnDescriptor> {\n const result = new Map<string, ColumnDescriptor>();\n for (const [typeName, codecId] of scalarTypeDescriptors) {\n const nativeType = codecLookup.targetTypesFor(codecId)?.[0];\n if (nativeType === undefined) continue;\n result.set(typeName, { codecId, nativeType });\n }\n return result;\n}\n\nexport function prismaContract(schemaPath: string, options: PrismaContractOptions): ContractConfig {\n return {\n source: {\n inputs: [schemaPath],\n load: async (context) => {\n const [absoluteSchemaPath] = context.resolvedInputs;\n if (absoluteSchemaPath === undefined) {\n throw new Error(\n 'prismaContract: context.resolvedInputs is empty. The CLI config loader should populate it positional-matched with source.inputs.',\n );\n }\n let schema: string;\n try {\n schema = await readFile(absoluteSchemaPath, 'utf-8');\n } catch (error) {\n const message = String(error);\n return notOk({\n summary: `Failed to read Prisma schema at \"${schemaPath}\"`,\n diagnostics: [\n {\n code: 'PSL_SCHEMA_READ_FAILED',\n message,\n sourceId: schemaPath,\n },\n ],\n meta: { schemaPath, absoluteSchemaPath, cause: message },\n });\n }\n\n const document = parsePslDocument({\n schema,\n sourceId: schemaPath,\n });\n\n const scalarTypeDescriptors = buildColumnDescriptorMap(\n context.scalarTypeDescriptors,\n context.codecLookup,\n );\n\n const interpreted = interpretPslDocumentToSqlContract({\n document,\n target: options.target,\n authoringContributions: context.authoringContributions,\n scalarTypeDescriptors,\n ...ifDefined(\n 'composedExtensionPacks',\n context.composedExtensionPacks.length > 0\n ? [...context.composedExtensionPacks]\n : undefined,\n ),\n ...ifDefined(\n 'composedExtensionPackRefs',\n options.composedExtensionPackRefs?.length\n ? options.composedExtensionPackRefs\n : undefined,\n ),\n controlMutationDefaults: context.controlMutationDefaults,\n });\n if (!interpreted.ok) {\n return interpreted;\n }\n\n return ok(interpreted.value);\n },\n },\n output: options.output ?? defaultOutputFromSchemaPath(schemaPath),\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider.mjs","names":[],"sources":["../src/provider.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ContractConfig } from '@prisma-next/config/config-types';\nimport { applySpecifierDefaultControlPolicy } from '@prisma-next/contract/apply-specifier-default-control-policy';\nimport type { ControlPolicy } from '@prisma-next/contract/types';\nimport type { CodecLookup } from '@prisma-next/framework-components/codec';\nimport type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';\nimport { parsePslDocument } from '@prisma-next/psl-parser';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { basename, extname } from 'pathe';\nimport { interpretPslDocumentToSqlContract } from './interpreter';\nimport type { ColumnDescriptor } from './psl-column-resolution';\n\nexport interface PrismaContractOptions {\n readonly output?: string;\n readonly target: TargetPackRef<'sql', string>;\n readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql', string>[];\n readonly defaultControlPolicy?: ControlPolicy;\n}\n\n/**\n * Derives the emit output path from the schema input path so artefacts land\n * colocated with the source (e.g. `src/contract/schema.prisma` →\n * `src/contract/contract.json`). The provider owns this because it is the\n * only layer that knows the input path; the upstream `normalizeContractConfig`\n * default is a last-resort fallback for providers that don't carry one.\n */\nfunction defaultOutputFromSchemaPath(schemaPath: string): string {\n const ext = extname(schemaPath);\n if (ext.length === 0) return `${schemaPath}.json`;\n const base = schemaPath.slice(0, -ext.length);\n // PSL schemas commonly use `schema.prisma`; the emitted JSON is called\n // `contract.json` to mirror the rest of the toolchain, not `schema.json`.\n // Match only the exact basename `schema` so files like `my-schema.prisma`\n // are not silently rewritten to `my-contract.json`.\n if (basename(base) === 'schema') {\n return `${base.slice(0, -'schema'.length)}contract.json`;\n }\n return `${base}.json`;\n}\n\nfunction buildColumnDescriptorMap(\n scalarTypeDescriptors: ReadonlyMap<string, string>,\n codecLookup: CodecLookup,\n): ReadonlyMap<string, ColumnDescriptor> {\n const result = new Map<string, ColumnDescriptor>();\n for (const [typeName, codecId] of scalarTypeDescriptors) {\n const nativeType = codecLookup.targetTypesFor(codecId)?.[0];\n if (nativeType === undefined) continue;\n result.set(typeName, { codecId, nativeType });\n }\n return result;\n}\n\nexport function prismaContract(schemaPath: string, options: PrismaContractOptions): ContractConfig {\n return {\n source: {\n inputs: [schemaPath],\n load: async (context) => {\n const [absoluteSchemaPath] = context.resolvedInputs;\n if (absoluteSchemaPath === undefined) {\n throw new Error(\n 'prismaContract: context.resolvedInputs is empty. The CLI config loader should populate it positional-matched with source.inputs.',\n );\n }\n let schema: string;\n try {\n schema = await readFile(absoluteSchemaPath, 'utf-8');\n } catch (error) {\n const message = String(error);\n return notOk({\n summary: `Failed to read Prisma schema at \"${schemaPath}\"`,\n diagnostics: [\n {\n code: 'PSL_SCHEMA_READ_FAILED',\n message,\n sourceId: schemaPath,\n },\n ],\n meta: { schemaPath, absoluteSchemaPath, cause: message },\n });\n }\n\n const document = parsePslDocument({\n schema,\n sourceId: schemaPath,\n });\n\n const scalarTypeDescriptors = buildColumnDescriptorMap(\n context.scalarTypeDescriptors,\n context.codecLookup,\n );\n\n const interpreted = interpretPslDocumentToSqlContract({\n document,\n target: options.target,\n authoringContributions: context.authoringContributions,\n scalarTypeDescriptors,\n ...ifDefined(\n 'composedExtensionPacks',\n context.composedExtensionPacks.length > 0\n ? [...context.composedExtensionPacks]\n : undefined,\n ),\n ...ifDefined(\n 'composedExtensionPackRefs',\n options.composedExtensionPackRefs?.length\n ? options.composedExtensionPackRefs\n : undefined,\n ),\n controlMutationDefaults: context.controlMutationDefaults,\n });\n if (!interpreted.ok) {\n return interpreted;\n }\n\n return ok(\n applySpecifierDefaultControlPolicy(interpreted.value, options.defaultControlPolicy),\n );\n },\n },\n output: options.output ?? defaultOutputFromSchemaPath(schemaPath),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2BA,SAAS,4BAA4B,YAA4B;CAC/D,MAAM,MAAM,QAAQ,UAAU;CAC9B,IAAI,IAAI,WAAW,GAAG,OAAO,GAAG,WAAW;CAC3C,MAAM,OAAO,WAAW,MAAM,GAAG,CAAC,IAAI,MAAM;CAK5C,IAAI,SAAS,IAAI,MAAM,UACrB,OAAO,GAAG,KAAK,MAAM,GAAG,EAAgB,EAAE;CAE5C,OAAO,GAAG,KAAK;AACjB;AAEA,SAAS,yBACP,uBACA,aACuC;CACvC,MAAM,yBAAS,IAAI,IAA8B;CACjD,KAAK,MAAM,CAAC,UAAU,YAAY,uBAAuB;EACvD,MAAM,aAAa,YAAY,eAAe,OAAO,IAAI;EACzD,IAAI,eAAe,KAAA,GAAW;EAC9B,OAAO,IAAI,UAAU;GAAE;GAAS;EAAW,CAAC;CAC9C;CACA,OAAO;AACT;AAEA,SAAgB,eAAe,YAAoB,SAAgD;CACjG,OAAO;EACL,QAAQ;GACN,QAAQ,CAAC,UAAU;GACnB,MAAM,OAAO,YAAY;IACvB,MAAM,CAAC,sBAAsB,QAAQ;IACrC,IAAI,uBAAuB,KAAA,GACzB,MAAM,IAAI,MACR,kIACF;IAEF,IAAI;IACJ,IAAI;KACF,SAAS,MAAM,SAAS,oBAAoB,OAAO;IACrD,SAAS,OAAO;KACd,MAAM,UAAU,OAAO,KAAK;KAC5B,OAAO,MAAM;MACX,SAAS,oCAAoC,WAAW;MACxD,aAAa,CACX;OACE,MAAM;OACN;OACA,UAAU;MACZ,CACF;MACA,MAAM;OAAE;OAAY;OAAoB,OAAO;MAAQ;KACzD,CAAC;IACH;IAEA,MAAM,WAAW,iBAAiB;KAChC;KACA,UAAU;IACZ,CAAC;IAED,MAAM,wBAAwB,yBAC5B,QAAQ,uBACR,QAAQ,WACV;IAEA,MAAM,cAAc,kCAAkC;KACpD;KACA,QAAQ,QAAQ;KAChB,wBAAwB,QAAQ;KAChC;KACA,GAAG,UACD,0BACA,QAAQ,uBAAuB,SAAS,IACpC,CAAC,GAAG,QAAQ,sBAAsB,IAClC,KAAA,CACN;KACA,GAAG,UACD,6BACA,QAAQ,2BAA2B,SAC/B,QAAQ,4BACR,KAAA,CACN;KACA,yBAAyB,QAAQ;IACnC,CAAC;IACD,IAAI,CAAC,YAAY,IACf,OAAO;IAGT,OAAO,GACL,mCAAmC,YAAY,OAAO,QAAQ,oBAAoB,CACpF;GACF;EACF;EACA,QAAQ,QAAQ,UAAU,4BAA4B,UAAU;CAClE;AACF"}
|
package/package.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/sql-contract-psl",
|
|
3
|
-
"version": "0.12.0-dev.
|
|
3
|
+
"version": "0.12.0-dev.32",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "PSL-to-SQL ContractIR interpreter for Prisma Next",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@prisma-next/config": "0.12.0-dev.
|
|
10
|
-
"@prisma-next/contract": "0.12.0-dev.
|
|
11
|
-
"@prisma-next/framework-components": "0.12.0-dev.
|
|
12
|
-
"@prisma-next/psl-parser": "0.12.0-dev.
|
|
13
|
-
"@prisma-next/sql-contract": "0.12.0-dev.
|
|
14
|
-
"@prisma-next/sql-contract-ts": "0.12.0-dev.
|
|
15
|
-
"@prisma-next/utils": "0.12.0-dev.
|
|
9
|
+
"@prisma-next/config": "0.12.0-dev.32",
|
|
10
|
+
"@prisma-next/contract": "0.12.0-dev.32",
|
|
11
|
+
"@prisma-next/framework-components": "0.12.0-dev.32",
|
|
12
|
+
"@prisma-next/psl-parser": "0.12.0-dev.32",
|
|
13
|
+
"@prisma-next/sql-contract": "0.12.0-dev.32",
|
|
14
|
+
"@prisma-next/sql-contract-ts": "0.12.0-dev.32",
|
|
15
|
+
"@prisma-next/utils": "0.12.0-dev.32",
|
|
16
16
|
"pathe": "^2.0.3"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@prisma-next/contract-authoring": "0.12.0-dev.
|
|
20
|
-
"@prisma-next/test-utils": "0.12.0-dev.
|
|
21
|
-
"@prisma-next/tsconfig": "0.12.0-dev.
|
|
22
|
-
"@prisma-next/tsdown": "0.12.0-dev.
|
|
19
|
+
"@prisma-next/contract-authoring": "0.12.0-dev.32",
|
|
20
|
+
"@prisma-next/test-utils": "0.12.0-dev.32",
|
|
21
|
+
"@prisma-next/tsconfig": "0.12.0-dev.32",
|
|
22
|
+
"@prisma-next/tsdown": "0.12.0-dev.32",
|
|
23
23
|
"arktype": "^2.2.0",
|
|
24
24
|
"tsdown": "0.22.0",
|
|
25
25
|
"typescript": "5.9.3",
|
package/src/interpreter.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
ContractField,
|
|
9
9
|
ContractModel,
|
|
10
10
|
ContractValueObject,
|
|
11
|
+
ControlPolicy,
|
|
11
12
|
} from '@prisma-next/contract/types';
|
|
12
13
|
import { crossRef } from '@prisma-next/contract/types';
|
|
13
14
|
import type {
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
mapFieldNamesToColumns,
|
|
58
59
|
parseAttributeFieldList,
|
|
59
60
|
parseConstraintMapArgument,
|
|
61
|
+
parseControlPolicyAttribute,
|
|
60
62
|
parseMapName,
|
|
61
63
|
parseObjectLiteralStringMap,
|
|
62
64
|
parseQuotedStringLiteral,
|
|
@@ -642,6 +644,8 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
642
644
|
: undefined;
|
|
643
645
|
const hasInlinePrimaryKey = primaryKey !== undefined;
|
|
644
646
|
let blockPrimaryKeyDeclared = false;
|
|
647
|
+
let controlPolicyDeclared = false;
|
|
648
|
+
let controlPolicy: ControlPolicy | undefined;
|
|
645
649
|
|
|
646
650
|
const resultBackrelationCandidates: ModelBackrelationCandidate[] = [];
|
|
647
651
|
for (const field of model.fields) {
|
|
@@ -728,6 +732,27 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
728
732
|
if (modelAttribute.name === 'discriminator' || modelAttribute.name === 'base') {
|
|
729
733
|
continue;
|
|
730
734
|
}
|
|
735
|
+
if (modelAttribute.name === 'control') {
|
|
736
|
+
if (controlPolicyDeclared) {
|
|
737
|
+
diagnostics.push({
|
|
738
|
+
code: 'PSL_DUPLICATE_ATTRIBUTE',
|
|
739
|
+
message: `\`@@control\` declared more than once on model "${model.name}".`,
|
|
740
|
+
sourceId,
|
|
741
|
+
span: modelAttribute.span,
|
|
742
|
+
});
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
controlPolicyDeclared = true;
|
|
746
|
+
const parsed = parseControlPolicyAttribute({
|
|
747
|
+
attribute: modelAttribute,
|
|
748
|
+
sourceId,
|
|
749
|
+
diagnostics,
|
|
750
|
+
});
|
|
751
|
+
if (parsed !== undefined) {
|
|
752
|
+
controlPolicy = parsed;
|
|
753
|
+
}
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
731
756
|
const attributeLabel = `Model "${model.name}" @@${modelAttribute.name}`;
|
|
732
757
|
if (modelAttribute.name === 'id') {
|
|
733
758
|
if (blockPrimaryKeyDeclared) {
|
|
@@ -1098,6 +1123,7 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
1098
1123
|
...(uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {}),
|
|
1099
1124
|
...(indexNodes.length > 0 ? { indexes: indexNodes } : {}),
|
|
1100
1125
|
...(foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {}),
|
|
1126
|
+
...ifDefined('control', controlPolicy),
|
|
1101
1127
|
},
|
|
1102
1128
|
fkRelationMetadata: resultFkRelationMetadata,
|
|
1103
1129
|
backrelationCandidates: resultBackrelationCandidates,
|
package/src/provider.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import type { ContractConfig } from '@prisma-next/config/config-types';
|
|
3
|
+
import { applySpecifierDefaultControlPolicy } from '@prisma-next/contract/apply-specifier-default-control-policy';
|
|
4
|
+
import type { ControlPolicy } from '@prisma-next/contract/types';
|
|
3
5
|
import type { CodecLookup } from '@prisma-next/framework-components/codec';
|
|
4
6
|
import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';
|
|
5
7
|
import { parsePslDocument } from '@prisma-next/psl-parser';
|
|
@@ -13,6 +15,7 @@ export interface PrismaContractOptions {
|
|
|
13
15
|
readonly output?: string;
|
|
14
16
|
readonly target: TargetPackRef<'sql', string>;
|
|
15
17
|
readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql', string>[];
|
|
18
|
+
readonly defaultControlPolicy?: ControlPolicy;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
/**
|
|
@@ -111,7 +114,9 @@ export function prismaContract(schemaPath: string, options: PrismaContractOption
|
|
|
111
114
|
return interpreted;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
|
-
return ok(
|
|
117
|
+
return ok(
|
|
118
|
+
applySpecifierDefaultControlPolicy(interpreted.value, options.defaultControlPolicy),
|
|
119
|
+
);
|
|
115
120
|
},
|
|
116
121
|
},
|
|
117
122
|
output: options.output ?? defaultOutputFromSchemaPath(schemaPath),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContractSourceDiagnostic } from '@prisma-next/config/config-types';
|
|
2
|
+
import type { ControlPolicy } from '@prisma-next/contract/types';
|
|
2
3
|
import type { PslAttribute, PslSpan } from '@prisma-next/psl-parser';
|
|
3
4
|
import { getPositionalArgument, parseQuotedStringLiteral } from '@prisma-next/psl-parser';
|
|
4
5
|
|
|
@@ -411,6 +412,71 @@ export function findDuplicateFieldName(fieldNames: readonly string[]): string |
|
|
|
411
412
|
return undefined;
|
|
412
413
|
}
|
|
413
414
|
|
|
415
|
+
const CONTROL_POLICY_LITERALS = [
|
|
416
|
+
'managed',
|
|
417
|
+
'tolerated',
|
|
418
|
+
'external',
|
|
419
|
+
'observed',
|
|
420
|
+
] as const satisfies readonly ControlPolicy[];
|
|
421
|
+
|
|
422
|
+
const CONTROL_POLICY_LITERAL_SET = new Set<string>(CONTROL_POLICY_LITERALS);
|
|
423
|
+
|
|
424
|
+
function isControlPolicyLiteral(value: string): value is ControlPolicy {
|
|
425
|
+
return CONTROL_POLICY_LITERAL_SET.has(value);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function parseControlPolicyAttribute(input: {
|
|
429
|
+
readonly attribute: PslAttribute;
|
|
430
|
+
readonly sourceId: string;
|
|
431
|
+
readonly diagnostics: ContractSourceDiagnostic[];
|
|
432
|
+
}): ControlPolicy | undefined {
|
|
433
|
+
const namedArgs = input.attribute.args.filter((arg) => arg.kind === 'named');
|
|
434
|
+
if (namedArgs.length > 0) {
|
|
435
|
+
input.diagnostics.push({
|
|
436
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
437
|
+
message:
|
|
438
|
+
'`@@control` does not accept named arguments; pass the policy positionally as `@@control(external)`.',
|
|
439
|
+
sourceId: input.sourceId,
|
|
440
|
+
span: input.attribute.span,
|
|
441
|
+
});
|
|
442
|
+
return undefined;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const positionalArgs = getPositionalArguments(input.attribute);
|
|
446
|
+
if (positionalArgs.length === 0) {
|
|
447
|
+
input.diagnostics.push({
|
|
448
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
449
|
+
message:
|
|
450
|
+
'`@@control` requires exactly one positional argument: `managed`, `tolerated`, `external`, or `observed`.',
|
|
451
|
+
sourceId: input.sourceId,
|
|
452
|
+
span: input.attribute.span,
|
|
453
|
+
});
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
if (positionalArgs.length > 1) {
|
|
457
|
+
input.diagnostics.push({
|
|
458
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
459
|
+
message: `\`@@control\` accepts exactly one positional argument; got ${positionalArgs.length}.`,
|
|
460
|
+
sourceId: input.sourceId,
|
|
461
|
+
span: input.attribute.span,
|
|
462
|
+
});
|
|
463
|
+
return undefined;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const token = unquoteStringLiteral(positionalArgs[0] ?? '').trim();
|
|
467
|
+
if (!isControlPolicyLiteral(token)) {
|
|
468
|
+
input.diagnostics.push({
|
|
469
|
+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
|
|
470
|
+
message: `\`@@control\` argument \`${token}\` is not a known policy. Allowed: \`managed\`, \`tolerated\`, \`external\`, \`observed\`.`,
|
|
471
|
+
sourceId: input.sourceId,
|
|
472
|
+
span: input.attribute.span,
|
|
473
|
+
});
|
|
474
|
+
return undefined;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return token;
|
|
478
|
+
}
|
|
479
|
+
|
|
414
480
|
export function mapFieldNamesToColumns(input: {
|
|
415
481
|
readonly modelName: string;
|
|
416
482
|
readonly fieldNames: readonly string[];
|