@prisma-next/extension-cipherstash 0.5.0-dev.87 → 0.6.0-dev.1
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 +17 -0
- package/dist/column-types.mjs +2 -2
- package/dist/column-types.mjs.map +1 -1
- package/dist/{constants-B_2TNvUi.mjs → constants-mSHfut_M.mjs} +2 -2
- package/dist/constants-mSHfut_M.mjs.map +1 -0
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +15 -111
- package/dist/control.mjs.map +1 -1
- package/dist/{descriptor-meta-BgQfZTAF.mjs → descriptor-meta-BGF8z8oy.mjs} +3 -3
- package/dist/descriptor-meta-BGF8z8oy.mjs.map +1 -0
- package/dist/middleware.mjs +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/runtime.mjs +1 -1
- package/package.json +21 -21
- package/src/{contract/authoring.ts → contract-authoring.ts} +2 -5
- package/src/{contract/contract.prisma → contract.prisma} +3 -3
- package/src/exports/column-types.ts +1 -1
- package/src/exports/control.ts +22 -48
- package/src/exports/migration.ts +1 -1
- package/src/extension-metadata/constants.ts +1 -1
- package/src/extension-metadata/descriptor-meta.ts +1 -1
- package/dist/constants-B_2TNvUi.mjs.map +0 -1
- package/dist/descriptor-meta-BgQfZTAF.mjs.map +0 -1
- /package/src/{contract/contract.d.ts → contract.d.ts} +0 -0
- /package/src/{contract/contract.json → contract.json} +0 -0
package/README.md
CHANGED
|
@@ -101,6 +101,19 @@ After `prisma-next migrate plan`, the user's repo gains:
|
|
|
101
101
|
|
|
102
102
|
`db apply` then runs CipherStash's migrations against the live database in the same transaction as any application-space migration emitted in the same `migrate` invocation.
|
|
103
103
|
|
|
104
|
+
## Authoring (maintainers)
|
|
105
|
+
|
|
106
|
+
The extension's contract + baseline migration are emitted on-disk inside this package using the same pipeline application authors use:
|
|
107
|
+
|
|
108
|
+
- `pnpm build:contract-space` — runs `prisma-next contract emit` to produce `src/contract.{json,d.ts}` from the PSL source at `src/contract.prisma`.
|
|
109
|
+
- `pnpm exec prisma-next migration plan --name <slug>` (run from this package directory) — scaffolds a new migration directory under `migrations/<dirName>/`. **Not chained into `pnpm build`**: `migration plan` is non-idempotent (each invocation generates a new timestamped directory), so it runs manually when the contract source changes — same convention application authors follow. The baseline migration's `migration.ts` is then hand-edited so that its `operations` getter installs the EQL bundle byte-for-byte plus the structural `cipherstash:*` no-op ops that register invariantIds for typed objects the bundle creates (see the comment in `migrations/20260601T0000_install_eql_bundle/migration.ts`).
|
|
110
|
+
- `pnpm tsx migrations/<dirName>/migration.ts` (run from this package directory) — re-emits `ops.json` + `migration.json` from the hand-edited subclass. Use `tsx`, not bare `node`, because the Migration subclass imports relative TypeScript siblings (`../../src/core/constants`, `../../src/core/eql-bundle`) which Node's native loader can't resolve without a TS-aware loader.
|
|
111
|
+
- `migrations/refs/head.json` is hand-pinned with the latest migration's `to` hash + `providedInvariants`.
|
|
112
|
+
|
|
113
|
+
The descriptor at `src/exports/control.ts` then JSON-imports those artefacts and synthesises the framework's `MigrationPackage` shape.
|
|
114
|
+
|
|
115
|
+
See [ADR 212 — Contract spaces](../../../docs/architecture%20docs/adrs/ADR%20212%20-%20Contract%20spaces.md) ("Contract-space package layout") for the canonical layout and rationale.
|
|
116
|
+
|
|
104
117
|
## Runtime usage
|
|
105
118
|
|
|
106
119
|
A worked end-to-end example lives at [`examples/cipherstash-integration/`](../../../examples/cipherstash-integration/) — schema, runtime composition, demo SDK stub, and an `insert` → `cipherstashEq` → `cipherstashIlike` → `decryptAll` flow against a real Postgres database.
|
|
@@ -151,3 +164,7 @@ See [`DEVELOPING.md`](./DEVELOPING.md) for source layout, design choices, and th
|
|
|
151
164
|
- [CipherStash](https://cipherstash.com) — managed application-layer encryption for Postgres.
|
|
152
165
|
- [Prisma Next Architecture Overview](../../../docs/Architecture%20Overview.md).
|
|
153
166
|
- [Extension Packs Naming and Layout](../../../docs/reference/Extension-Packs-Naming-and-Layout.md).
|
|
167
|
+
- [ADR 212 — Contract spaces](../../../docs/architecture%20docs/adrs/ADR%20212%20-%20Contract%20spaces.md)
|
|
168
|
+
- [ADR 213 — Codec lifecycle hooks](../../../docs/architecture%20docs/adrs/ADR%20213%20-%20Codec%20lifecycle%20hooks.md)
|
|
169
|
+
- [Subsystem doc — Ecosystem Extensions & Packs](../../../docs/architecture%20docs/subsystems/6.%20Ecosystem%20Extensions%20%26%20Packs.md)
|
|
170
|
+
- Contract-space layout rule: [`.cursor/rules/contract-space-package-layout.mdc`](../../../.cursor/rules/contract-space-package-layout.mdc)
|
package/dist/column-types.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { a as EQL_V2_ENCRYPTED_TYPE, i as CIPHERSTASH_STRING_CODEC_ID } from "./constants-
|
|
1
|
+
import { a as EQL_V2_ENCRYPTED_TYPE, i as CIPHERSTASH_STRING_CODEC_ID } from "./constants-mSHfut_M.mjs";
|
|
2
2
|
//#region src/exports/column-types.ts
|
|
3
3
|
/**
|
|
4
4
|
* TS contract factory for cipherstash-encrypted string columns.
|
|
5
5
|
*
|
|
6
6
|
* Counterpart to the PSL constructor `cipherstash.EncryptedString({...})`
|
|
7
|
-
* registered in `../contract
|
|
7
|
+
* registered in `../contract-authoring`. Both factories produce the same
|
|
8
8
|
* `ColumnTypeDescriptor` shape so PSL- and TS-authored contracts emit
|
|
9
9
|
* byte-identical `contract.json` (verified by the parity fixture under
|
|
10
10
|
* `test/integration/test/authoring/parity/cipherstash-encrypted-string/`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"column-types.mjs","names":[],"sources":["../src/exports/column-types.ts"],"sourcesContent":["/**\n * TS contract factory for cipherstash-encrypted string columns.\n *\n * Counterpart to the PSL constructor `cipherstash.EncryptedString({...})`\n * registered in `../contract
|
|
1
|
+
{"version":3,"file":"column-types.mjs","names":[],"sources":["../src/exports/column-types.ts"],"sourcesContent":["/**\n * TS contract factory for cipherstash-encrypted string columns.\n *\n * Counterpart to the PSL constructor `cipherstash.EncryptedString({...})`\n * registered in `../contract-authoring`. Both factories produce the same\n * `ColumnTypeDescriptor` shape so PSL- and TS-authored contracts emit\n * byte-identical `contract.json` (verified by the parity fixture under\n * `test/integration/test/authoring/parity/cipherstash-encrypted-string/`).\n *\n * Both flags default to `true` — searchable encryption is the\n * legitimate default for an extension whose entire reason for existing\n * is to make encrypted columns queryable. Users who want storage-only\n * encryption opt out explicitly: `encryptedString({ equality: false,\n * freeTextSearch: false })`. Mirrors the PSL constructor's `true`\n * defaults declared via `AuthoringArgRef.default`.\n */\n\nimport {\n CIPHERSTASH_STRING_CODEC_ID,\n EQL_V2_ENCRYPTED_TYPE,\n} from '../extension-metadata/constants';\n\n/**\n * Search-mode parameters for `encryptedString({...})`. Both flags are\n * optional and default to `true` when omitted.\n */\nexport interface EncryptedStringOptions {\n readonly equality?: boolean;\n readonly freeTextSearch?: boolean;\n}\n\nexport interface EncryptedStringColumnDescriptor {\n readonly codecId: typeof CIPHERSTASH_STRING_CODEC_ID;\n readonly nativeType: typeof EQL_V2_ENCRYPTED_TYPE;\n readonly typeParams: {\n readonly equality: boolean;\n readonly freeTextSearch: boolean;\n };\n}\n\n/**\n * `encryptedString({ equality?, freeTextSearch? })` — TS contract\n * factory that lowers to a `ColumnTypeDescriptor` with the\n * `cipherstash/string@1` codec and the `eql_v2_encrypted` Postgres\n * native type. The two boolean flags become `typeParams.equality` and\n * `typeParams.freeTextSearch`. Both default to `true`.\n *\n * The shape matches what the PSL constructor\n * `cipherstash.EncryptedString({...})` lowers to, byte-for-byte.\n */\nexport function encryptedString(\n options: EncryptedStringOptions = {},\n): EncryptedStringColumnDescriptor {\n return {\n codecId: CIPHERSTASH_STRING_CODEC_ID,\n nativeType: EQL_V2_ENCRYPTED_TYPE,\n typeParams: {\n equality: options.equality ?? true,\n freeTextSearch: options.freeTextSearch ?? true,\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,SAAgB,gBACd,UAAkC,EAAE,EACH;CACjC,OAAO;EACL,SAAS;EACT,YAAY;EACZ,YAAY;GACV,UAAU,QAAQ,YAAY;GAC9B,gBAAgB,QAAQ,kBAAkB;GAC3C;EACF"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* (once published, an invariantId cannot be renamed).
|
|
11
11
|
*
|
|
12
12
|
* The space identifier `'cipherstash'` is what the framework writes to
|
|
13
|
-
* `migrations/cipherstash/`
|
|
13
|
+
* the consuming app's `migrations/cipherstash/` directory and what the marker table's
|
|
14
14
|
* `space` column carries for CipherStash-owned rows.
|
|
15
15
|
*/
|
|
16
16
|
const CIPHERSTASH_SPACE_ID = "cipherstash";
|
|
@@ -43,4 +43,4 @@ const CIPHERSTASH_BASELINE_MIGRATION_NAME = "20260601T0000_install_eql_bundle";
|
|
|
43
43
|
//#endregion
|
|
44
44
|
export { EQL_V2_ENCRYPTED_TYPE as a, CIPHERSTASH_STRING_CODEC_ID as i, CIPHERSTASH_EXTENSION_VERSION as n, CIPHERSTASH_SPACE_ID as r, CIPHERSTASH_BASELINE_MIGRATION_NAME as t };
|
|
45
45
|
|
|
46
|
-
//# sourceMappingURL=constants-
|
|
46
|
+
//# sourceMappingURL=constants-mSHfut_M.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants-mSHfut_M.mjs","names":[],"sources":["../src/extension-metadata/constants.ts"],"sourcesContent":["/**\n * Static names and identifiers used across CipherStash's contract space.\n *\n * Centralising the strings here so:\n * - the contract IR (`./contract`), the migration ops (`./migrations`),\n * the head ref (`./head-ref`), and the descriptor (`../exports/control`)\n * all reference the same values without typos;\n * - the `cipherstash:*` invariantId namespace is locked in one place\n * (once published, an invariantId cannot be renamed).\n *\n * The space identifier `'cipherstash'` is what the framework writes to\n * the consuming app's `migrations/cipherstash/` directory and what the marker table's\n * `space` column carries for CipherStash-owned rows.\n */\n\nexport const CIPHERSTASH_SPACE_ID = 'cipherstash';\n\n/**\n * Version advertised by both `cipherstashPackMeta.version` (control plane)\n * and the SDK-bound `SqlRuntimeExtensionDescriptor` (runtime plane).\n *\n * Single source of truth so the descriptor surfaces and the contract-emit\n * pack metadata cannot drift apart; consumed downstream by capability\n * gating and contract round-trips.\n */\nexport const CIPHERSTASH_EXTENSION_VERSION = '0.0.1' as const;\n\n/**\n * Codec id the application-side `Encrypted<string>` lowering targets.\n * Lives here so the codec lifecycle hook (which emits\n * `add_search_config` / `remove_search_config` ops on field events) and\n * the descriptor's `controlPlaneHooks` wiring share the same constant.\n */\nexport const CIPHERSTASH_STRING_CODEC_ID = 'cipherstash/string@1';\n\n/** Schema CipherStash installs its functions/operators/casts/types into. */\nexport const EQL_V2_SCHEMA = 'eql_v2';\n\n/** Configuration table used by EQL's per-column index configuration. */\nexport const EQL_V2_CONFIGURATION_TABLE = 'eql_v2_configuration';\n\n/** Enum type backing the `state` column on `eql_v2_configuration`. */\nexport const EQL_V2_CONFIGURATION_STATE_TYPE = 'eql_v2_configuration_state';\n\n/** JSONB-domain composite type user `Encrypted<string>` columns reference. */\nexport const EQL_V2_ENCRYPTED_TYPE = 'eql_v2_encrypted';\n\n/**\n * Migration directory name for the baseline.\n *\n * Per the framework's per-space layout convention this name is\n * preserved verbatim when the framework writes the package to\n * `migrations/cipherstash/<this-name>/` in the user's repo.\n */\nexport const CIPHERSTASH_BASELINE_MIGRATION_NAME = '20260601T0000_install_eql_bundle';\n\n/**\n * `cipherstash:*` invariantIds emitted by the baseline migration. Each\n * `cipherstash:*` id, once published, is immutable: downstream\n * consumers (other extensions, the marker table) reference them by\n * literal string match.\n *\n * Today the baseline emits a single op (`installBundle`); the bundle\n * SQL is the source of truth for every typed object it creates inside\n * the `eql_v2` schema. New bundle versions or additional structural\n * ops will mint new `cipherstash:*` ids alongside this entry.\n */\nexport const CIPHERSTASH_INVARIANTS = {\n installBundle: 'cipherstash:install-eql-bundle-v1',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAa,uBAAuB;;;;;;;;;AAUpC,MAAa,gCAAgC;;;;;;;AAQ7C,MAAa,8BAA8B;;AAY3C,MAAa,wBAAwB;;;;;;;;AASrC,MAAa,sCAAsC"}
|
package/dist/control.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/exports/control.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/exports/control.ts"],"mappings":";;;cA6DM,8BAAA,EAAgC,6BAAA"}
|
package/dist/control.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { i as CIPHERSTASH_STRING_CODEC_ID,
|
|
2
|
-
import { t as cipherstashPackMeta } from "./descriptor-meta-
|
|
1
|
+
import { i as CIPHERSTASH_STRING_CODEC_ID, t as CIPHERSTASH_BASELINE_MIGRATION_NAME } from "./constants-mSHfut_M.mjs";
|
|
2
|
+
import { t as cipherstashPackMeta } from "./descriptor-meta-BGF8z8oy.mjs";
|
|
3
3
|
import { n as cipherstashRemoveSearchConfig, t as cipherstashAddSearchConfig } from "./call-classes-CSvD7w8U.mjs";
|
|
4
|
-
import {
|
|
5
|
-
//#region migrations/
|
|
4
|
+
import { contractSpaceFromJson } from "@prisma-next/migration-tools/spaces";
|
|
5
|
+
//#region migrations/20260601T0000_install_eql_bundle/migration.json
|
|
6
6
|
var migration_default = {
|
|
7
7
|
from: null,
|
|
8
8
|
to: "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4",
|
|
@@ -106,7 +106,7 @@ var migration_default = {
|
|
|
106
106
|
migrationHash: "sha256:c9ac07fca95d6159ecb21841514b87fbc51c2cf7ba55f2f28ae4c07f25781ab2"
|
|
107
107
|
};
|
|
108
108
|
//#endregion
|
|
109
|
-
//#region migrations/
|
|
109
|
+
//#region migrations/20260601T0000_install_eql_bundle/ops.json
|
|
110
110
|
var ops_default = [{
|
|
111
111
|
"id": "cipherstash.install-eql-bundle",
|
|
112
112
|
"label": "Install EQL bundle (functions, operators, casts, op classes, schema, types)",
|
|
@@ -127,13 +127,13 @@ var ops_default = [{
|
|
|
127
127
|
}]
|
|
128
128
|
}];
|
|
129
129
|
//#endregion
|
|
130
|
-
//#region migrations/
|
|
130
|
+
//#region migrations/refs/head.json
|
|
131
131
|
var head_default = {
|
|
132
132
|
hash: "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4",
|
|
133
133
|
invariants: ["cipherstash:install-eql-bundle-v1"]
|
|
134
134
|
};
|
|
135
135
|
//#endregion
|
|
136
|
-
//#region src/contract
|
|
136
|
+
//#region src/contract.json
|
|
137
137
|
var contract_default = {
|
|
138
138
|
schemaVersion: "1",
|
|
139
139
|
targetFamily: "sql",
|
|
@@ -296,112 +296,16 @@ const cipherstashStringCodecHooks = {
|
|
|
296
296
|
expandNativeType
|
|
297
297
|
};
|
|
298
298
|
//#endregion
|
|
299
|
-
//#region src/exports/contract-space-typing.ts
|
|
300
|
-
function fail(field, value) {
|
|
301
|
-
throw new Error(`cipherstash contract-space JSON is missing or malformed at "${field}" (saw ${typeof value}). The on-disk JSON drifted from the framework's expected shape — re-run \`prisma-next contract emit\` and \`prisma-next migration plan\` for the cipherstash space.`);
|
|
302
|
-
}
|
|
303
|
-
function isRecord(value) {
|
|
304
|
-
return typeof value === "object" && value !== null;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Narrow a JSON-imported `contract.json` value to `Contract<SqlStorage>`.
|
|
308
|
-
* Checks the discriminators the framework relies on at descriptor
|
|
309
|
-
* registration time; everything else is consumed downstream by the
|
|
310
|
-
* runner / verifier, which performs its own validation.
|
|
311
|
-
*/
|
|
312
|
-
function asCipherstashContract(value) {
|
|
313
|
-
if (!isRecord(value)) fail("<root>", value);
|
|
314
|
-
if (typeof value["target"] !== "string") fail("target", value["target"]);
|
|
315
|
-
if (typeof value["targetFamily"] !== "string") fail("targetFamily", value["targetFamily"]);
|
|
316
|
-
const storage = value["storage"];
|
|
317
|
-
if (!isRecord(storage)) fail("storage", storage);
|
|
318
|
-
if (typeof storage["storageHash"] !== "string") fail("storage.storageHash", storage["storageHash"]);
|
|
319
|
-
return value;
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Narrow a JSON-imported `migration.json` value to `MigrationMetadata`.
|
|
323
|
-
* The framework's runner consumes the metadata for ordering /
|
|
324
|
-
* provenance; missing `to` or a non-string `migrationHash` here means
|
|
325
|
-
* a non-emitted artefact slipped into the import path.
|
|
326
|
-
*/
|
|
327
|
-
function asCipherstashMigrationMetadata(value) {
|
|
328
|
-
if (!isRecord(value)) fail("<root>", value);
|
|
329
|
-
if (typeof value["to"] !== "string") fail("to", value["to"]);
|
|
330
|
-
if (typeof value["migrationHash"] !== "string") fail("migrationHash", value["migrationHash"]);
|
|
331
|
-
return value;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Narrow a JSON-imported `ops.json` value to
|
|
335
|
-
* `readonly MigrationPlanOperation[]`. Checks each entry carries the
|
|
336
|
-
* canonical `id` / `operationClass` discriminator so a malformed entry
|
|
337
|
-
* doesn't reach the planner.
|
|
338
|
-
*/
|
|
339
|
-
function asCipherstashMigrationOps(value) {
|
|
340
|
-
if (!Array.isArray(value)) fail("<root>", value);
|
|
341
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
342
|
-
const entry = value[index];
|
|
343
|
-
if (!isRecord(entry)) fail(`[${index}]`, entry);
|
|
344
|
-
if (typeof entry["id"] !== "string") fail(`[${index}].id`, entry["id"]);
|
|
345
|
-
if (typeof entry["operationClass"] !== "string") fail(`[${index}].operationClass`, entry["operationClass"]);
|
|
346
|
-
}
|
|
347
|
-
return value;
|
|
348
|
-
}
|
|
349
|
-
//#endregion
|
|
350
299
|
//#region src/exports/control.ts
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
* `prisma-next migration plan` → `<package>/migrations/cipherstash/<dir>/...`
|
|
359
|
-
*
|
|
360
|
-
* The descriptor wires those JSON artefacts via JSON-import declarations
|
|
361
|
-
* so they flow through the consuming application's module resolver
|
|
362
|
-
* without filesystem assumptions, and synthesises the canonical
|
|
363
|
-
* {@link import('@prisma-next/migration-tools/package').MigrationPackage}
|
|
364
|
-
* shape (gaining `dirPath` from `import.meta.url`) for the framework's
|
|
365
|
-
* runner / verifier to consume.
|
|
366
|
-
*
|
|
367
|
-
* Wired surfaces:
|
|
368
|
-
*
|
|
369
|
-
* - `contractSpace.{contractJson,migrations,headRef}` — sourced from
|
|
370
|
-
* the on-disk artefacts emitted by `build:contract-space`.
|
|
371
|
-
* - `types.codecTypes.controlPlaneHooks[CIPHERSTASH_STRING_CODEC_ID]`
|
|
372
|
-
* — the lifecycle hook the SQL planner extracts via
|
|
373
|
-
* `extractCodecControlHooks` and inlines into the application's
|
|
374
|
-
* migration via `planFieldEventOperations`. Implements
|
|
375
|
-
* `add_search_config` / `remove_search_config` / rotate behaviour
|
|
376
|
-
* for `searchable: true` `Encrypted<string>` columns.
|
|
377
|
-
*
|
|
378
|
-
* @see docs/architecture docs/adrs/ADR 211 - Contract spaces.md
|
|
379
|
-
* (on-disk-in-package authoring convention).
|
|
380
|
-
* @see packages/3-extensions/test-contract-space/src/exports/control.ts
|
|
381
|
-
* (reference model).
|
|
382
|
-
*/
|
|
383
|
-
/**
|
|
384
|
-
* Resolve a migration package's on-disk path from this descriptor module's
|
|
385
|
-
* URL. The framework's runner uses `dirPath` for diagnostic messages and
|
|
386
|
-
* to locate sibling files (e.g. `start-contract.json` for non-baseline
|
|
387
|
-
* migrations); pinning it from `import.meta.url` keeps the value correct
|
|
388
|
-
* regardless of where the consuming application installs the package
|
|
389
|
-
* (workspace, node_modules, bundled, etc.).
|
|
390
|
-
*/
|
|
391
|
-
function resolveMigrationDirPath(dirName) {
|
|
392
|
-
return fileURLToPath(new URL(`../../migrations/${CIPHERSTASH_SPACE_ID}/${dirName}/`, import.meta.url));
|
|
393
|
-
}
|
|
394
|
-
const baselinePackage = {
|
|
395
|
-
dirName: CIPHERSTASH_BASELINE_MIGRATION_NAME,
|
|
396
|
-
dirPath: resolveMigrationDirPath(CIPHERSTASH_BASELINE_MIGRATION_NAME),
|
|
397
|
-
metadata: asCipherstashMigrationMetadata(migration_default),
|
|
398
|
-
ops: asCipherstashMigrationOps(ops_default)
|
|
399
|
-
};
|
|
400
|
-
const cipherstashContractSpace = {
|
|
401
|
-
contractJson: asCipherstashContract(contract_default),
|
|
402
|
-
migrations: [baselinePackage],
|
|
300
|
+
const cipherstashContractSpace = contractSpaceFromJson({
|
|
301
|
+
contractJson: contract_default,
|
|
302
|
+
migrations: [{
|
|
303
|
+
dirName: CIPHERSTASH_BASELINE_MIGRATION_NAME,
|
|
304
|
+
metadata: migration_default,
|
|
305
|
+
ops: ops_default
|
|
306
|
+
}],
|
|
403
307
|
headRef: head_default
|
|
404
|
-
};
|
|
308
|
+
});
|
|
405
309
|
const cipherstashExtensionDescriptor = {
|
|
406
310
|
...cipherstashPackMeta,
|
|
407
311
|
contractSpace: cipherstashContractSpace,
|
package/dist/control.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.mjs","names":["baselineMetadata","baselineOps","contractJson"],"sources":["../migrations/cipherstash/20260601T0000_install_eql_bundle/migration.json","../migrations/cipherstash/20260601T0000_install_eql_bundle/ops.json","../migrations/cipherstash/refs/head.json","../src/contract/contract.json","../src/migration/cipherstash-codec.ts","../src/exports/contract-space-typing.ts","../src/exports/control.ts"],"sourcesContent":["","","","","/**\n * Control hooks for the `cipherstash:string@1` codec.\n *\n * Implements `CodecControlHooks.onFieldEvent`. Reacts to per-field\n * added / dropped / altered events as the *application* emitter diffs\n * the prior contract against the new contract; the returned Calls flow\n * through the SQL planner's IR alongside structural DDL and render as\n * `cipherstashAddSearchConfig({...})` / `cipherstashRemoveSearchConfig({...})`\n * calls in the user's `migration.ts` (ADR 195 two-renderer pattern).\n *\n * Trigger: a field uses the `cipherstash:string@1` codec. The planner\n * already dispatches per `(table, field)` based on the field's\n * `codecId` (new field for `'added'` / `'altered'`, prior field for\n * `'dropped'`), so this hook only fires when a cipherstash field is\n * involved. Per field the hook emits **one\n * `cipherstashAddSearchConfig` Call per enabled flag** in `typeParams`\n * (and one `cipherstashRemoveSearchConfig` Call per previously-enabled\n * flag on drop / altered-off).\n *\n * Flag → EQL index mapping:\n *\n * - `equality: true` → `'unique'` index\n * - `freeTextSearch: true` → `'match'` index\n *\n * One Call per flag (rather than a single multi-statement Call per\n * field) keeps each Call independently invertible by a paired\n * `cipherstashRemoveSearchConfig` Call carrying the same index name,\n * and the op-graph stays per-flag granular for diffing.\n *\n * `'altered'` events decompose into per-flag deltas:\n * - flag flipped on → emit `cipherstashAddSearchConfig({...})`.\n * - flag flipped off → emit `cipherstashRemoveSearchConfig({...})`.\n * - flag unchanged → no Call.\n *\n * `invariantId` template (carried on the Call's `toOp()` output):\n * `cipherstash-codec:<table>.<field>:<action>:<index>@v1`\n * `<action>` ∈ `'add-search-config' | 'remove-search-config'`,\n * `<index>` ∈ `'unique' | 'match'`.\n * Stable across regenerations because every input is deterministic.\n */\n\nimport type { CodecControlHooks, FieldEventContext } from '@prisma-next/family-sql/control';\nimport type { OpFactoryCall } from '@prisma-next/framework-components/control';\nimport { CIPHERSTASH_STRING_CODEC_ID } from '../extension-metadata/constants';\nimport {\n type CipherstashSearchIndex,\n cipherstashAddSearchConfig,\n cipherstashRemoveSearchConfig,\n} from './call-classes';\n\ntype FlagName = 'equality' | 'freeTextSearch';\n\nconst FLAG_TO_INDEX: Readonly<Record<FlagName, CipherstashSearchIndex>> = {\n equality: 'unique',\n freeTextSearch: 'match',\n};\n\nconst ALL_FLAGS: ReadonlyArray<FlagName> = ['equality', 'freeTextSearch'];\n\nfunction isEnabled(\n typeParams: Readonly<Record<string, unknown>> | undefined,\n flag: FlagName,\n): boolean {\n return typeParams !== undefined && typeParams[flag] === true;\n}\n\n/**\n * Hook entry point. Called by `planFieldEventOperations` for every per-\n * field delta dispatched to `cipherstash:string@1`. Pure and\n * synchronous; callers replay it deterministically when re-emitting.\n */\nfunction onFieldEvent(\n event: 'added' | 'dropped' | 'altered',\n ctx: FieldEventContext,\n): readonly OpFactoryCall[] {\n const { tableName, fieldName, priorField, newField } = ctx;\n\n if (event === 'added') {\n if (newField === undefined) return [];\n const calls: OpFactoryCall[] = [];\n for (const flag of ALL_FLAGS) {\n if (isEnabled(newField.typeParams, flag)) {\n calls.push(\n cipherstashAddSearchConfig({\n table: tableName,\n column: fieldName,\n index: FLAG_TO_INDEX[flag],\n }),\n );\n }\n }\n return calls;\n }\n\n if (event === 'dropped') {\n if (priorField === undefined) return [];\n const calls: OpFactoryCall[] = [];\n for (const flag of ALL_FLAGS) {\n if (isEnabled(priorField.typeParams, flag)) {\n calls.push(\n cipherstashRemoveSearchConfig({\n table: tableName,\n column: fieldName,\n index: FLAG_TO_INDEX[flag],\n }),\n );\n }\n }\n return calls;\n }\n\n if (priorField === undefined || newField === undefined) return [];\n const calls: OpFactoryCall[] = [];\n for (const flag of ALL_FLAGS) {\n const before = isEnabled(priorField.typeParams, flag);\n const after = isEnabled(newField.typeParams, flag);\n if (after && !before) {\n calls.push(\n cipherstashAddSearchConfig({\n table: tableName,\n column: fieldName,\n index: FLAG_TO_INDEX[flag],\n }),\n );\n } else if (before && !after) {\n calls.push(\n cipherstashRemoveSearchConfig({\n table: tableName,\n column: fieldName,\n index: FLAG_TO_INDEX[flag],\n }),\n );\n }\n }\n return calls;\n}\n\n/**\n * The DDL type for an `Encrypted<string>` column is always\n * `eql_v2_encrypted` regardless of any `typeParams` flags: the\n * search-config wiring is delivered by the codec hook's\n * `cipherstashAddSearchConfig` Calls (separate rows in\n * `eql_v2_configuration`), not by the column type itself. Returning\n * `nativeType` unchanged tells the planner \"no expansion required\" —\n * see `expandParameterizedTypeSql` in\n * `packages/3-targets/3-targets/postgres/src/core/migrations/planner-ddl-builders.ts`,\n * which only requires this hook to *exist* for any column carrying\n * `typeParams`. Without it, the planner refuses to render the column\n * (the existing arktype-json extension wires the same identity hook).\n */\nconst expandNativeType: NonNullable<CodecControlHooks['expandNativeType']> = ({ nativeType }) =>\n nativeType;\n\nexport const cipherstashStringCodecHooks: CodecControlHooks = { onFieldEvent, expandNativeType };\n\n/** Re-export the codec id alongside the hooks so wiring sites import them together. */\nexport { CIPHERSTASH_STRING_CODEC_ID };\n","/**\n * Typed-narrowing helpers for the on-disk contract-space JSON artefacts\n * the cipherstash control descriptor wires into its\n * `SqlControlExtensionDescriptor`.\n *\n * JSON-imported values come back as widened, structurally-typed\n * objects: branded fields (`storageHash: StorageHashBase<string>`) and\n * discriminated unions (`MigrationPlanOperation['operationClass']`)\n * fall back to plain strings, so a direct assignment into the\n * descriptor surfaces is a type error. The cipherstash MVP previously\n * suppressed that error with `as unknown as X` triple-casts, which\n * silently masks any future shape drift between the emitted JSON and\n * the in-package descriptor.\n *\n * This module replaces the blind casts with thin runtime assertions\n * that fail fast on drift and narrow the JSON inputs to the framework\n * types in a single, auditable place. The assertions are intentionally\n * minimal — they check the canonical discriminator fields (`storageHash`,\n * `space`, `dirName`, `operationClass`, …) rather than re-validating\n * the whole emitter contract — which is enough to surface schema-level\n * drift while keeping the descriptor module light.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport type { MigrationPlanOperation } from '@prisma-next/framework-components/control';\nimport type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\n\nfunction fail(field: string, value: unknown): never {\n throw new Error(\n `cipherstash contract-space JSON is missing or malformed at \"${field}\" (saw ${typeof value}). The on-disk JSON drifted from the framework's expected shape — re-run \\`prisma-next contract emit\\` and \\`prisma-next migration plan\\` for the cipherstash space.`,\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Narrow a JSON-imported `contract.json` value to `Contract<SqlStorage>`.\n * Checks the discriminators the framework relies on at descriptor\n * registration time; everything else is consumed downstream by the\n * runner / verifier, which performs its own validation.\n */\nexport function asCipherstashContract(value: unknown): Contract<SqlStorage> {\n if (!isRecord(value)) fail('<root>', value);\n if (typeof value['target'] !== 'string') fail('target', value['target']);\n if (typeof value['targetFamily'] !== 'string') fail('targetFamily', value['targetFamily']);\n const storage = value['storage'];\n if (!isRecord(storage)) fail('storage', storage);\n if (typeof storage['storageHash'] !== 'string')\n fail('storage.storageHash', storage['storageHash']);\n return value as unknown as Contract<SqlStorage>;\n}\n\n/**\n * Narrow a JSON-imported `migration.json` value to `MigrationMetadata`.\n * The framework's runner consumes the metadata for ordering /\n * provenance; missing `to` or a non-string `migrationHash` here means\n * a non-emitted artefact slipped into the import path.\n */\nexport function asCipherstashMigrationMetadata(value: unknown): MigrationMetadata {\n if (!isRecord(value)) fail('<root>', value);\n if (typeof value['to'] !== 'string') fail('to', value['to']);\n if (typeof value['migrationHash'] !== 'string') fail('migrationHash', value['migrationHash']);\n return value as unknown as MigrationMetadata;\n}\n\n/**\n * Narrow a JSON-imported `ops.json` value to\n * `readonly MigrationPlanOperation[]`. Checks each entry carries the\n * canonical `id` / `operationClass` discriminator so a malformed entry\n * doesn't reach the planner.\n */\nexport function asCipherstashMigrationOps(value: unknown): readonly MigrationPlanOperation[] {\n if (!Array.isArray(value)) fail('<root>', value);\n for (let index = 0; index < value.length; index += 1) {\n const entry = value[index];\n if (!isRecord(entry)) fail(`[${index}]`, entry);\n if (typeof entry['id'] !== 'string') fail(`[${index}].id`, entry['id']);\n if (typeof entry['operationClass'] !== 'string') {\n fail(`[${index}].operationClass`, entry['operationClass']);\n }\n }\n return value as unknown as readonly MigrationPlanOperation[];\n}\n","/**\n * Control-plane descriptor for the CipherStash extension.\n *\n * **On-disk-in-package authoring.** The extension's contract +\n * migrations are emitted by the same pipeline application authors use:\n *\n * `prisma-next contract emit` → `<package>/src/contract/contract.{json,d.ts}`\n * `prisma-next migration plan` → `<package>/migrations/cipherstash/<dir>/...`\n *\n * The descriptor wires those JSON artefacts via JSON-import declarations\n * so they flow through the consuming application's module resolver\n * without filesystem assumptions, and synthesises the canonical\n * {@link import('@prisma-next/migration-tools/package').MigrationPackage}\n * shape (gaining `dirPath` from `import.meta.url`) for the framework's\n * runner / verifier to consume.\n *\n * Wired surfaces:\n *\n * - `contractSpace.{contractJson,migrations,headRef}` — sourced from\n * the on-disk artefacts emitted by `build:contract-space`.\n * - `types.codecTypes.controlPlaneHooks[CIPHERSTASH_STRING_CODEC_ID]`\n * — the lifecycle hook the SQL planner extracts via\n * `extractCodecControlHooks` and inlines into the application's\n * migration via `planFieldEventOperations`. Implements\n * `add_search_config` / `remove_search_config` / rotate behaviour\n * for `searchable: true` `Encrypted<string>` columns.\n *\n * @see docs/architecture docs/adrs/ADR 211 - Contract spaces.md\n * (on-disk-in-package authoring convention).\n * @see packages/3-extensions/test-contract-space/src/exports/control.ts\n * (reference model).\n */\n\nimport { fileURLToPath } from 'node:url';\nimport type { Contract } from '@prisma-next/contract/types';\nimport type { SqlControlExtensionDescriptor } from '@prisma-next/family-sql/control';\nimport type { ContractSpace } from '@prisma-next/framework-components/control';\nimport type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport baselineMetadata from '../../migrations/cipherstash/20260601T0000_install_eql_bundle/migration.json' with {\n type: 'json',\n};\nimport baselineOps from '../../migrations/cipherstash/20260601T0000_install_eql_bundle/ops.json' with {\n type: 'json',\n};\nimport headRef from '../../migrations/cipherstash/refs/head.json' with { type: 'json' };\nimport contractJson from '../contract/contract.json' with { type: 'json' };\nimport {\n CIPHERSTASH_BASELINE_MIGRATION_NAME,\n CIPHERSTASH_SPACE_ID,\n CIPHERSTASH_STRING_CODEC_ID,\n} from '../extension-metadata/constants';\nimport { cipherstashPackMeta } from '../extension-metadata/descriptor-meta';\nimport { cipherstashStringCodecHooks } from '../migration/cipherstash-codec';\nimport {\n asCipherstashContract,\n asCipherstashMigrationMetadata,\n asCipherstashMigrationOps,\n} from './contract-space-typing';\n\n/**\n * Resolve a migration package's on-disk path from this descriptor module's\n * URL. The framework's runner uses `dirPath` for diagnostic messages and\n * to locate sibling files (e.g. `start-contract.json` for non-baseline\n * migrations); pinning it from `import.meta.url` keeps the value correct\n * regardless of where the consuming application installs the package\n * (workspace, node_modules, bundled, etc.).\n */\nfunction resolveMigrationDirPath(dirName: string): string {\n return fileURLToPath(\n new URL(`../../migrations/${CIPHERSTASH_SPACE_ID}/${dirName}/`, import.meta.url),\n );\n}\n\nconst baselinePackage: OnDiskMigrationPackage = {\n dirName: CIPHERSTASH_BASELINE_MIGRATION_NAME,\n dirPath: resolveMigrationDirPath(CIPHERSTASH_BASELINE_MIGRATION_NAME),\n metadata: asCipherstashMigrationMetadata(baselineMetadata),\n ops: asCipherstashMigrationOps(baselineOps),\n};\n\nconst cipherstashContractSpace: ContractSpace<Contract<SqlStorage>> = {\n contractJson: asCipherstashContract(contractJson),\n migrations: [baselinePackage],\n headRef,\n};\n\nconst cipherstashExtensionDescriptor: SqlControlExtensionDescriptor<'postgres'> = {\n // Spread pack-meta first so it contributes `kind` / `id` / `familyId`\n // / `targetId` / `version` / `authoring` / `types.{codecTypes,storage}`\n // — then overlay the contract-space block and the codec lifecycle\n // hook on top. The two `types.codecTypes` slots (`codecInstances`\n // from pack-meta, `controlPlaneHooks` from this descriptor) coexist\n // on the same path and are merged below.\n ...cipherstashPackMeta,\n contractSpace: cipherstashContractSpace,\n /**\n * Free-form `types.codecTypes.controlPlaneHooks` block — the SQL\n * family's `extractCodecControlHooks` (in `@prisma-next/family-sql/\n * control`) finds hooks via duck-typing on this exact path. Mirrors\n * pgvector's wiring at `packages/3-extensions/pgvector/src/exports/\n * control.ts`.\n */\n types: {\n ...cipherstashPackMeta.types,\n codecTypes: {\n ...cipherstashPackMeta.types.codecTypes,\n controlPlaneHooks: {\n [CIPHERSTASH_STRING_CODEC_ID]: cipherstashStringCodecHooks,\n },\n },\n },\n create: () => ({\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n }),\n};\n\nexport { cipherstashExtensionDescriptor };\nexport default cipherstashExtensionDescriptor;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AIoDA,MAAM,gBAAoE;CACxE,UAAU;CACV,gBAAgB;CACjB;AAED,MAAM,YAAqC,CAAC,YAAY,iBAAiB;AAEzE,SAAS,UACP,YACA,MACS;CACT,OAAO,eAAe,KAAA,KAAa,WAAW,UAAU;;;;;;;AAQ1D,SAAS,aACP,OACA,KAC0B;CAC1B,MAAM,EAAE,WAAW,WAAW,YAAY,aAAa;CAEvD,IAAI,UAAU,SAAS;EACrB,IAAI,aAAa,KAAA,GAAW,OAAO,EAAE;EACrC,MAAM,QAAyB,EAAE;EACjC,KAAK,MAAM,QAAQ,WACjB,IAAI,UAAU,SAAS,YAAY,KAAK,EACtC,MAAM,KACJ,2BAA2B;GACzB,OAAO;GACP,QAAQ;GACR,OAAO,cAAc;GACtB,CAAC,CACH;EAGL,OAAO;;CAGT,IAAI,UAAU,WAAW;EACvB,IAAI,eAAe,KAAA,GAAW,OAAO,EAAE;EACvC,MAAM,QAAyB,EAAE;EACjC,KAAK,MAAM,QAAQ,WACjB,IAAI,UAAU,WAAW,YAAY,KAAK,EACxC,MAAM,KACJ,8BAA8B;GAC5B,OAAO;GACP,QAAQ;GACR,OAAO,cAAc;GACtB,CAAC,CACH;EAGL,OAAO;;CAGT,IAAI,eAAe,KAAA,KAAa,aAAa,KAAA,GAAW,OAAO,EAAE;CACjE,MAAM,QAAyB,EAAE;CACjC,KAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,SAAS,UAAU,WAAW,YAAY,KAAK;EACrD,MAAM,QAAQ,UAAU,SAAS,YAAY,KAAK;EAClD,IAAI,SAAS,CAAC,QACZ,MAAM,KACJ,2BAA2B;GACzB,OAAO;GACP,QAAQ;GACR,OAAO,cAAc;GACtB,CAAC,CACH;OACI,IAAI,UAAU,CAAC,OACpB,MAAM,KACJ,8BAA8B;GAC5B,OAAO;GACP,QAAQ;GACR,OAAO,cAAc;GACtB,CAAC,CACH;;CAGL,OAAO;;;;;;;;;;;;;;;AAgBT,MAAM,oBAAwE,EAAE,iBAC9E;AAEF,MAAa,8BAAiD;CAAE;CAAc;CAAkB;;;AC7HhG,SAAS,KAAK,OAAe,OAAuB;CAClD,MAAM,IAAI,MACR,+DAA+D,MAAM,SAAS,OAAO,MAAM,sKAC5F;;AAGH,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU;;;;;;;;AAShD,SAAgB,sBAAsB,OAAsC;CAC1E,IAAI,CAAC,SAAS,MAAM,EAAE,KAAK,UAAU,MAAM;CAC3C,IAAI,OAAO,MAAM,cAAc,UAAU,KAAK,UAAU,MAAM,UAAU;CACxE,IAAI,OAAO,MAAM,oBAAoB,UAAU,KAAK,gBAAgB,MAAM,gBAAgB;CAC1F,MAAM,UAAU,MAAM;CACtB,IAAI,CAAC,SAAS,QAAQ,EAAE,KAAK,WAAW,QAAQ;CAChD,IAAI,OAAO,QAAQ,mBAAmB,UACpC,KAAK,uBAAuB,QAAQ,eAAe;CACrD,OAAO;;;;;;;;AAST,SAAgB,+BAA+B,OAAmC;CAChF,IAAI,CAAC,SAAS,MAAM,EAAE,KAAK,UAAU,MAAM;CAC3C,IAAI,OAAO,MAAM,UAAU,UAAU,KAAK,MAAM,MAAM,MAAM;CAC5D,IAAI,OAAO,MAAM,qBAAqB,UAAU,KAAK,iBAAiB,MAAM,iBAAiB;CAC7F,OAAO;;;;;;;;AAST,SAAgB,0BAA0B,OAAmD;CAC3F,IAAI,CAAC,MAAM,QAAQ,MAAM,EAAE,KAAK,UAAU,MAAM;CAChD,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACpD,MAAM,QAAQ,MAAM;EACpB,IAAI,CAAC,SAAS,MAAM,EAAE,KAAK,IAAI,MAAM,IAAI,MAAM;EAC/C,IAAI,OAAO,MAAM,UAAU,UAAU,KAAK,IAAI,MAAM,OAAO,MAAM,MAAM;EACvE,IAAI,OAAO,MAAM,sBAAsB,UACrC,KAAK,IAAI,MAAM,mBAAmB,MAAM,kBAAkB;;CAG9D,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChBT,SAAS,wBAAwB,SAAyB;CACxD,OAAO,cACL,IAAI,IAAI,oBAAoB,qBAAqB,GAAG,QAAQ,IAAI,OAAO,KAAK,IAAI,CACjF;;AAGH,MAAM,kBAA0C;CAC9C,SAAS;CACT,SAAS,wBAAwB,oCAAoC;CACrE,UAAU,+BAA+BA,kBAAiB;CAC1D,KAAK,0BAA0BC,YAAY;CAC5C;AAED,MAAM,2BAAgE;CACpE,cAAc,sBAAsBC,iBAAa;CACjD,YAAY,CAAC,gBAAgB;CAC7B,SAAA;CACD;AAED,MAAM,iCAA4E;CAOhF,GAAG;CACH,eAAe;;;;;;;;CAQf,OAAO;EACL,GAAG,oBAAoB;EACvB,YAAY;GACV,GAAG,oBAAoB,MAAM;GAC7B,mBAAmB,GAChB,8BAA8B,6BAChC;GACF;EACF;CACD,eAAe;EACb,UAAU;EACV,UAAU;EACX;CACF"}
|
|
1
|
+
{"version":3,"file":"control.mjs","names":["baselineMetadata","baselineOps"],"sources":["../migrations/20260601T0000_install_eql_bundle/migration.json","../migrations/20260601T0000_install_eql_bundle/ops.json","../migrations/refs/head.json","../src/contract.json","../src/migration/cipherstash-codec.ts","../src/exports/control.ts"],"sourcesContent":["","","","","/**\n * Control hooks for the `cipherstash:string@1` codec.\n *\n * Implements `CodecControlHooks.onFieldEvent`. Reacts to per-field\n * added / dropped / altered events as the *application* emitter diffs\n * the prior contract against the new contract; the returned Calls flow\n * through the SQL planner's IR alongside structural DDL and render as\n * `cipherstashAddSearchConfig({...})` / `cipherstashRemoveSearchConfig({...})`\n * calls in the user's `migration.ts` (ADR 195 two-renderer pattern).\n *\n * Trigger: a field uses the `cipherstash:string@1` codec. The planner\n * already dispatches per `(table, field)` based on the field's\n * `codecId` (new field for `'added'` / `'altered'`, prior field for\n * `'dropped'`), so this hook only fires when a cipherstash field is\n * involved. Per field the hook emits **one\n * `cipherstashAddSearchConfig` Call per enabled flag** in `typeParams`\n * (and one `cipherstashRemoveSearchConfig` Call per previously-enabled\n * flag on drop / altered-off).\n *\n * Flag → EQL index mapping:\n *\n * - `equality: true` → `'unique'` index\n * - `freeTextSearch: true` → `'match'` index\n *\n * One Call per flag (rather than a single multi-statement Call per\n * field) keeps each Call independently invertible by a paired\n * `cipherstashRemoveSearchConfig` Call carrying the same index name,\n * and the op-graph stays per-flag granular for diffing.\n *\n * `'altered'` events decompose into per-flag deltas:\n * - flag flipped on → emit `cipherstashAddSearchConfig({...})`.\n * - flag flipped off → emit `cipherstashRemoveSearchConfig({...})`.\n * - flag unchanged → no Call.\n *\n * `invariantId` template (carried on the Call's `toOp()` output):\n * `cipherstash-codec:<table>.<field>:<action>:<index>@v1`\n * `<action>` ∈ `'add-search-config' | 'remove-search-config'`,\n * `<index>` ∈ `'unique' | 'match'`.\n * Stable across regenerations because every input is deterministic.\n */\n\nimport type { CodecControlHooks, FieldEventContext } from '@prisma-next/family-sql/control';\nimport type { OpFactoryCall } from '@prisma-next/framework-components/control';\nimport { CIPHERSTASH_STRING_CODEC_ID } from '../extension-metadata/constants';\nimport {\n type CipherstashSearchIndex,\n cipherstashAddSearchConfig,\n cipherstashRemoveSearchConfig,\n} from './call-classes';\n\ntype FlagName = 'equality' | 'freeTextSearch';\n\nconst FLAG_TO_INDEX: Readonly<Record<FlagName, CipherstashSearchIndex>> = {\n equality: 'unique',\n freeTextSearch: 'match',\n};\n\nconst ALL_FLAGS: ReadonlyArray<FlagName> = ['equality', 'freeTextSearch'];\n\nfunction isEnabled(\n typeParams: Readonly<Record<string, unknown>> | undefined,\n flag: FlagName,\n): boolean {\n return typeParams !== undefined && typeParams[flag] === true;\n}\n\n/**\n * Hook entry point. Called by `planFieldEventOperations` for every per-\n * field delta dispatched to `cipherstash:string@1`. Pure and\n * synchronous; callers replay it deterministically when re-emitting.\n */\nfunction onFieldEvent(\n event: 'added' | 'dropped' | 'altered',\n ctx: FieldEventContext,\n): readonly OpFactoryCall[] {\n const { tableName, fieldName, priorField, newField } = ctx;\n\n if (event === 'added') {\n if (newField === undefined) return [];\n const calls: OpFactoryCall[] = [];\n for (const flag of ALL_FLAGS) {\n if (isEnabled(newField.typeParams, flag)) {\n calls.push(\n cipherstashAddSearchConfig({\n table: tableName,\n column: fieldName,\n index: FLAG_TO_INDEX[flag],\n }),\n );\n }\n }\n return calls;\n }\n\n if (event === 'dropped') {\n if (priorField === undefined) return [];\n const calls: OpFactoryCall[] = [];\n for (const flag of ALL_FLAGS) {\n if (isEnabled(priorField.typeParams, flag)) {\n calls.push(\n cipherstashRemoveSearchConfig({\n table: tableName,\n column: fieldName,\n index: FLAG_TO_INDEX[flag],\n }),\n );\n }\n }\n return calls;\n }\n\n if (priorField === undefined || newField === undefined) return [];\n const calls: OpFactoryCall[] = [];\n for (const flag of ALL_FLAGS) {\n const before = isEnabled(priorField.typeParams, flag);\n const after = isEnabled(newField.typeParams, flag);\n if (after && !before) {\n calls.push(\n cipherstashAddSearchConfig({\n table: tableName,\n column: fieldName,\n index: FLAG_TO_INDEX[flag],\n }),\n );\n } else if (before && !after) {\n calls.push(\n cipherstashRemoveSearchConfig({\n table: tableName,\n column: fieldName,\n index: FLAG_TO_INDEX[flag],\n }),\n );\n }\n }\n return calls;\n}\n\n/**\n * The DDL type for an `Encrypted<string>` column is always\n * `eql_v2_encrypted` regardless of any `typeParams` flags: the\n * search-config wiring is delivered by the codec hook's\n * `cipherstashAddSearchConfig` Calls (separate rows in\n * `eql_v2_configuration`), not by the column type itself. Returning\n * `nativeType` unchanged tells the planner \"no expansion required\" —\n * see `expandParameterizedTypeSql` in\n * `packages/3-targets/3-targets/postgres/src/core/migrations/planner-ddl-builders.ts`,\n * which only requires this hook to *exist* for any column carrying\n * `typeParams`. Without it, the planner refuses to render the column\n * (the existing arktype-json extension wires the same identity hook).\n */\nconst expandNativeType: NonNullable<CodecControlHooks['expandNativeType']> = ({ nativeType }) =>\n nativeType;\n\nexport const cipherstashStringCodecHooks: CodecControlHooks = { onFieldEvent, expandNativeType };\n\n/** Re-export the codec id alongside the hooks so wiring sites import them together. */\nexport { CIPHERSTASH_STRING_CODEC_ID };\n","/**\n * Control-plane descriptor for the CipherStash extension.\n *\n * **Contract-space package layout.** The extension's contract +\n * migrations are emitted by the same pipeline application authors use:\n *\n * `prisma-next contract emit` → `<package>/src/contract.{json,d.ts}`\n * `prisma-next migration plan` → `<package>/migrations/<dir>/...`\n *\n * The descriptor wires those JSON artefacts via JSON-import declarations\n * so they flow through the consuming application's module resolver\n * without filesystem assumptions, and synthesises the canonical\n * {@link import('@prisma-next/framework-components/control').MigrationPackage}\n * shape for the framework's runner / verifier to consume.\n *\n * Wired surfaces:\n *\n * - `contractSpace.{contractJson,migrations,headRef}` — sourced from\n * the on-disk artefacts emitted by `build:contract-space`.\n * - `types.codecTypes.controlPlaneHooks[CIPHERSTASH_STRING_CODEC_ID]`\n * — the lifecycle hook the SQL planner extracts via\n * `extractCodecControlHooks` and inlines into the application's\n * migration via `planFieldEventOperations`. Implements\n * `add_search_config` / `remove_search_config` / rotate behaviour\n * for `searchable: true` `Encrypted<string>` columns.\n *\n * @see docs/architecture docs/adrs/ADR 212 - Contract spaces.md\n * (contract-space package layout convention).\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport type { SqlControlExtensionDescriptor } from '@prisma-next/family-sql/control';\nimport { contractSpaceFromJson } from '@prisma-next/migration-tools/spaces';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport baselineMetadata from '../../migrations/20260601T0000_install_eql_bundle/migration.json' with {\n type: 'json',\n};\nimport baselineOps from '../../migrations/20260601T0000_install_eql_bundle/ops.json' with {\n type: 'json',\n};\nimport headRef from '../../migrations/refs/head.json' with { type: 'json' };\nimport contractJson from '../contract.json' with { type: 'json' };\nimport {\n CIPHERSTASH_BASELINE_MIGRATION_NAME,\n CIPHERSTASH_STRING_CODEC_ID,\n} from '../extension-metadata/constants';\nimport { cipherstashPackMeta } from '../extension-metadata/descriptor-meta';\nimport { cipherstashStringCodecHooks } from '../migration/cipherstash-codec';\n\nconst cipherstashContractSpace = contractSpaceFromJson<Contract<SqlStorage>>({\n contractJson,\n migrations: [\n {\n dirName: CIPHERSTASH_BASELINE_MIGRATION_NAME,\n metadata: baselineMetadata,\n ops: baselineOps,\n },\n ],\n headRef,\n});\n\nconst cipherstashExtensionDescriptor: SqlControlExtensionDescriptor<'postgres'> = {\n // Spread pack-meta first so it contributes `kind` / `id` / `familyId`\n // / `targetId` / `version` / `authoring` / `types.{codecTypes,storage}`\n // — then overlay the contract-space block and the codec lifecycle\n // hook on top. The two `types.codecTypes` slots (`codecInstances`\n // from pack-meta, `controlPlaneHooks` from this descriptor) coexist\n // on the same path and are merged below.\n ...cipherstashPackMeta,\n contractSpace: cipherstashContractSpace,\n /**\n * Free-form `types.codecTypes.controlPlaneHooks` block — the SQL\n * family's `extractCodecControlHooks` (in `@prisma-next/family-sql/\n * control`) finds hooks via duck-typing on this exact path. Mirrors\n * pgvector's wiring at `packages/3-extensions/pgvector/src/exports/\n * control.ts`.\n */\n types: {\n ...cipherstashPackMeta.types,\n codecTypes: {\n ...cipherstashPackMeta.types.codecTypes,\n controlPlaneHooks: {\n [CIPHERSTASH_STRING_CODEC_ID]: cipherstashStringCodecHooks,\n },\n },\n },\n create: () => ({\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n }),\n};\n\nexport { cipherstashExtensionDescriptor };\nexport default cipherstashExtensionDescriptor;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AIoDA,MAAM,gBAAoE;CACxE,UAAU;CACV,gBAAgB;CACjB;AAED,MAAM,YAAqC,CAAC,YAAY,iBAAiB;AAEzE,SAAS,UACP,YACA,MACS;CACT,OAAO,eAAe,KAAA,KAAa,WAAW,UAAU;;;;;;;AAQ1D,SAAS,aACP,OACA,KAC0B;CAC1B,MAAM,EAAE,WAAW,WAAW,YAAY,aAAa;CAEvD,IAAI,UAAU,SAAS;EACrB,IAAI,aAAa,KAAA,GAAW,OAAO,EAAE;EACrC,MAAM,QAAyB,EAAE;EACjC,KAAK,MAAM,QAAQ,WACjB,IAAI,UAAU,SAAS,YAAY,KAAK,EACtC,MAAM,KACJ,2BAA2B;GACzB,OAAO;GACP,QAAQ;GACR,OAAO,cAAc;GACtB,CAAC,CACH;EAGL,OAAO;;CAGT,IAAI,UAAU,WAAW;EACvB,IAAI,eAAe,KAAA,GAAW,OAAO,EAAE;EACvC,MAAM,QAAyB,EAAE;EACjC,KAAK,MAAM,QAAQ,WACjB,IAAI,UAAU,WAAW,YAAY,KAAK,EACxC,MAAM,KACJ,8BAA8B;GAC5B,OAAO;GACP,QAAQ;GACR,OAAO,cAAc;GACtB,CAAC,CACH;EAGL,OAAO;;CAGT,IAAI,eAAe,KAAA,KAAa,aAAa,KAAA,GAAW,OAAO,EAAE;CACjE,MAAM,QAAyB,EAAE;CACjC,KAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,SAAS,UAAU,WAAW,YAAY,KAAK;EACrD,MAAM,QAAQ,UAAU,SAAS,YAAY,KAAK;EAClD,IAAI,SAAS,CAAC,QACZ,MAAM,KACJ,2BAA2B;GACzB,OAAO;GACP,QAAQ;GACR,OAAO,cAAc;GACtB,CAAC,CACH;OACI,IAAI,UAAU,CAAC,OACpB,MAAM,KACJ,8BAA8B;GAC5B,OAAO;GACP,QAAQ;GACR,OAAO,cAAc;GACtB,CAAC,CACH;;CAGL,OAAO;;;;;;;;;;;;;;;AAgBT,MAAM,oBAAwE,EAAE,iBAC9E;AAEF,MAAa,8BAAiD;CAAE;CAAc;CAAkB;;;ACxGhG,MAAM,2BAA2B,sBAA4C;CAC3E,cAAA;CACA,YAAY,CACV;EACE,SAAS;EACT,UAAUA;EACV,KAAKC;EACN,CACF;CACD,SAAA;CACD,CAAC;AAEF,MAAM,iCAA4E;CAOhF,GAAG;CACH,eAAe;;;;;;;;CAQf,OAAO;EACL,GAAG,oBAAoB;EACvB,YAAY;GACV,GAAG,oBAAoB,MAAM;GAC7B,mBAAmB,GAChB,8BAA8B,6BAChC;GACF;EACF;CACD,eAAe;EACb,UAAU;EACV,UAAU;EACX;CACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as EQL_V2_ENCRYPTED_TYPE, i as CIPHERSTASH_STRING_CODEC_ID, n as CIPHERSTASH_EXTENSION_VERSION, r as CIPHERSTASH_SPACE_ID } from "./constants-
|
|
1
|
+
import { a as EQL_V2_ENCRYPTED_TYPE, i as CIPHERSTASH_STRING_CODEC_ID, n as CIPHERSTASH_EXTENSION_VERSION, r as CIPHERSTASH_SPACE_ID } from "./constants-mSHfut_M.mjs";
|
|
2
2
|
import { CodecImpl } from "@prisma-next/framework-components/codec";
|
|
3
|
-
//#region src/contract
|
|
3
|
+
//#region src/contract-authoring.ts
|
|
4
4
|
const cipherstashAuthoringTypes = { cipherstash: { EncryptedString: {
|
|
5
5
|
kind: "typeConstructor",
|
|
6
6
|
args: [{
|
|
@@ -126,4 +126,4 @@ const cipherstashPackMeta = {
|
|
|
126
126
|
//#endregion
|
|
127
127
|
export { cipherstashPackMeta as t };
|
|
128
128
|
|
|
129
|
-
//# sourceMappingURL=descriptor-meta-
|
|
129
|
+
//# sourceMappingURL=descriptor-meta-BGF8z8oy.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor-meta-BGF8z8oy.mjs","names":[],"sources":["../src/contract-authoring.ts","../src/extension-metadata/codec-metadata.ts","../src/extension-metadata/descriptor-meta.ts"],"sourcesContent":["/**\n * Authoring contributions for the cipherstash extension.\n *\n * Registers `cipherstash.EncryptedString({ equality?, freeTextSearch? })`\n * as a namespaced PSL type constructor. The same descriptor lowers a\n * PSL field-type expression like `cipherstash.EncryptedString({ equality:\n * true })` and a TS factory call like `encryptedString({ equality: true })`\n * (see `../exports/column-types`) to an identical `ColumnTypeDescriptor`\n * so PSL- and TS-authored contracts emit byte-identical `contract.json`.\n *\n * Mirrors `packages/3-extensions/pgvector/src/core/authoring.ts`. The\n * cipherstash variant differs in three respects:\n * (a) `cipherstash` is the namespace,\n * (b) the constructor takes a single OPTIONAL object argument with two\n * optional booleans (so `cipherstash.EncryptedString()`,\n * `cipherstash.EncryptedString({})`, and the fully-spelled\n * `cipherstash.EncryptedString({ equality: true, freeTextSearch: true })`\n * all parse), and\n * (c) both flags default to `true` — searchable encryption is the\n * legitimate default for an extension whose entire reason for\n * existing is to make encrypted columns queryable. Users who want\n * storage-only encryption opt out explicitly:\n * `cipherstash.EncryptedString({ equality: false, freeTextSearch: false })`.\n */\n\nimport type { AuthoringTypeNamespace } from '@prisma-next/framework-components/authoring';\nimport { CIPHERSTASH_STRING_CODEC_ID, EQL_V2_ENCRYPTED_TYPE } from './extension-metadata/constants';\n\nexport const cipherstashAuthoringTypes = {\n cipherstash: {\n EncryptedString: {\n kind: 'typeConstructor',\n args: [\n {\n kind: 'object',\n name: 'options',\n optional: true,\n properties: {\n equality: { kind: 'boolean', optional: true },\n freeTextSearch: { kind: 'boolean', optional: true },\n },\n },\n ],\n output: {\n codecId: CIPHERSTASH_STRING_CODEC_ID,\n nativeType: EQL_V2_ENCRYPTED_TYPE,\n typeParams: {\n equality: { kind: 'arg', index: 0, path: ['equality'], default: true },\n freeTextSearch: {\n kind: 'arg',\n index: 0,\n path: ['freeTextSearch'],\n default: true,\n },\n },\n },\n },\n },\n} as const satisfies AuthoringTypeNamespace;\n","/**\n * SDK-free codec used in pack-meta (`cipherstashPackMeta.types.codecTypes\n * .codecInstances`). Pack-meta consumers only read codec *metadata*\n * (`typeId`, `targetTypes`, `traits`, `renderOutputType`) at contract\n * emit time — they never call `encode`/`decode`.\n *\n * The SDK-bound runtime codec for actual `encode`/`decode` lives in\n * `../execution/codec-runtime`; it is resolved through\n * `RuntimeParameterizedCodecDescriptor.factory` at runtime instead of\n * through pack-meta's `codecInstances`.\n *\n * Keeping the SDK-free metadata in its own module — and *not* importing\n * the runtime `CipherstashStringCodec` class — preserves the control\n * vs runtime split. Control-plane consumers (`exports/control.ts`,\n * `exports/pack.ts`) pull this file but never touch the envelope, the\n * SDK interface, or the bulk-encrypt middleware. The bundling-isolation\n * test pins this property by snapshotting that the control entry's\n * chunk graph does not transitively load `envelope-*.mjs`.\n *\n * `encode`/`decode` throw with a clear hint in the misuse case so\n * accidental wiring of the metadata codec into a real runtime path\n * surfaces immediately instead of silently no-op'ing.\n */\n\nimport type { JsonValue } from '@prisma-next/contract/types';\nimport { type AnyCodecDescriptor, CodecImpl } from '@prisma-next/framework-components/codec';\nimport { CIPHERSTASH_STRING_CODEC_ID, EQL_V2_ENCRYPTED_TYPE } from './constants';\n\nconst METADATA_DESCRIPTOR: AnyCodecDescriptor = {\n codecId: CIPHERSTASH_STRING_CODEC_ID,\n traits: [],\n targetTypes: [EQL_V2_ENCRYPTED_TYPE],\n meta: { db: { sql: { postgres: { nativeType: EQL_V2_ENCRYPTED_TYPE } } } },\n paramsSchema: {\n '~standard': {\n version: 1,\n vendor: 'cipherstash',\n validate: (value: unknown) => ({ value }),\n },\n },\n isParameterized: false,\n renderOutputType: () => 'EncryptedString',\n factory: () => () => {\n throw new Error('cipherstash codec: metadata descriptor factory is not callable');\n },\n};\n\nclass CipherstashStringCodecMetadata extends CodecImpl<\n typeof CIPHERSTASH_STRING_CODEC_ID,\n readonly [],\n unknown,\n unknown\n> {\n async encode(): Promise<unknown> {\n throw new Error(\n 'cipherstash codec: encode called on the pack-meta metadata codec. ' +\n 'Construct a runtime descriptor via `createCipherstashRuntimeDescriptor({ sdk })` and use that instead.',\n );\n }\n\n async decode(): Promise<unknown> {\n throw new Error(\n 'cipherstash codec: decode called on the pack-meta metadata codec. ' +\n 'Construct a runtime descriptor via `createCipherstashRuntimeDescriptor({ sdk })` and use that instead.',\n );\n }\n\n encodeJson(): JsonValue {\n return { $encryptedString: '<opaque>' };\n }\n\n decodeJson(): unknown {\n throw new Error(\n 'cipherstash codec: decodeJson is not supported; envelopes do not round-trip through JSON.',\n );\n }\n}\n\nexport const cipherstashStringCodecMetadata = new CipherstashStringCodecMetadata(\n METADATA_DESCRIPTOR,\n);\n","/**\n * Pack metadata for the cipherstash extension.\n *\n * Mirrors `packages/3-extensions/pgvector/src/extension-metadata/descriptor-meta.ts` —\n * the metadata block that gets serialized into `contract.json`'s\n * `extensionPacks.cipherstash` slot at emit time.\n *\n * SDK-free: the runtime descriptor layers SDK-bound codec instances on\n * top at execution time. The `codecInstances` slot here uses the\n * metadata-only\n * codec from `./codec-metadata` because pack-meta consumers only read\n * codec metadata (typeId, targetTypes, traits, renderOutputType);\n * runtime encode/decode always go through the SDK-bound codec produced\n * by `RuntimeParameterizedCodecDescriptor.factory` (see\n * `./parameterized`).\n *\n * The control descriptor in `../exports/control.ts` spreads this pack\n * meta so the framework's contract emitter sees `authoring`,\n * `types.codecTypes.codecInstances`, and `types.storage` alongside\n * the contract-space and codec-lifecycle-hooks blocks already wired\n * by the codec lifecycle hook block.\n */\n\nimport { cipherstashAuthoringTypes } from '../contract-authoring';\nimport { cipherstashStringCodecMetadata } from './codec-metadata';\nimport {\n CIPHERSTASH_EXTENSION_VERSION,\n CIPHERSTASH_SPACE_ID,\n CIPHERSTASH_STRING_CODEC_ID,\n EQL_V2_ENCRYPTED_TYPE,\n} from './constants';\n\nexport { CIPHERSTASH_EXTENSION_VERSION };\n\nexport const cipherstashPackMeta = {\n kind: 'extension',\n id: CIPHERSTASH_SPACE_ID,\n familyId: 'sql',\n targetId: 'postgres',\n version: CIPHERSTASH_EXTENSION_VERSION,\n authoring: {\n type: cipherstashAuthoringTypes,\n },\n types: {\n codecTypes: {\n codecInstances: [cipherstashStringCodecMetadata],\n // `renderOutputType` returns the bare type name `EncryptedString`\n // for parameterized cipherstash columns; the contract emitter\n // needs to import the type alongside that occurrence so the\n // generated `.d.ts` typechecks cleanly. Mirrors pgvector's\n // `Vector` typeImports declaration.\n typeImports: [\n {\n package: '@prisma-next/extension-cipherstash/runtime',\n named: 'EncryptedString',\n alias: 'EncryptedString',\n },\n ],\n },\n queryOperationTypes: {\n import: {\n package: '@prisma-next/extension-cipherstash/operation-types',\n named: 'QueryOperationTypes',\n alias: 'CipherstashQueryOperationTypes',\n },\n },\n storage: [\n {\n typeId: CIPHERSTASH_STRING_CODEC_ID,\n familyId: 'sql',\n targetId: 'postgres',\n nativeType: EQL_V2_ENCRYPTED_TYPE,\n },\n ],\n },\n} as const;\n"],"mappings":";;;AA4BA,MAAa,4BAA4B,EACvC,aAAa,EACX,iBAAiB;CACf,MAAM;CACN,MAAM,CACJ;EACE,MAAM;EACN,MAAM;EACN,UAAU;EACV,YAAY;GACV,UAAU;IAAE,MAAM;IAAW,UAAU;IAAM;GAC7C,gBAAgB;IAAE,MAAM;IAAW,UAAU;IAAM;GACpD;EACF,CACF;CACD,QAAQ;EACN,SAAS;EACT,YAAY;EACZ,YAAY;GACV,UAAU;IAAE,MAAM;IAAO,OAAO;IAAG,MAAM,CAAC,WAAW;IAAE,SAAS;IAAM;GACtE,gBAAgB;IACd,MAAM;IACN,OAAO;IACP,MAAM,CAAC,iBAAiB;IACxB,SAAS;IACV;GACF;EACF;CACF,EACF,EACF;;;AC9BD,MAAM,sBAA0C;CAC9C,SAAS;CACT,QAAQ,EAAE;CACV,aAAa,CAAC,sBAAsB;CACpC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,uBAAuB,EAAE,EAAE,EAAE;CAC1E,cAAc,EACZ,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,WAAoB,EAAE,OAAO;EACzC,EACF;CACD,iBAAiB;CACjB,wBAAwB;CACxB,qBAAqB;EACnB,MAAM,IAAI,MAAM,iEAAiE;;CAEpF;AAED,IAAM,iCAAN,cAA6C,UAK3C;CACA,MAAM,SAA2B;EAC/B,MAAM,IAAI,MACR,2KAED;;CAGH,MAAM,SAA2B;EAC/B,MAAM,IAAI,MACR,2KAED;;CAGH,aAAwB;EACtB,OAAO,EAAE,kBAAkB,YAAY;;CAGzC,aAAsB;EACpB,MAAM,IAAI,MACR,4FACD;;;AAIL,MAAa,iCAAiC,IAAI,+BAChD,oBACD;;;;;;;;;;;;;;;;;;;;;;;;;AC9CD,MAAa,sBAAsB;CACjC,MAAM;CACN,IAAI;CACJ,UAAU;CACV,UAAU;CACV,SAAS;CACT,WAAW,EACT,MAAM,2BACP;CACD,OAAO;EACL,YAAY;GACV,gBAAgB,CAAC,+BAA+B;GAMhD,aAAa,CACX;IACE,SAAS;IACT,OAAO;IACP,OAAO;IACR,CACF;GACF;EACD,qBAAqB,EACnB,QAAQ;GACN,SAAS;GACT,OAAO;GACP,OAAO;GACR,EACF;EACD,SAAS,CACP;GACE,QAAQ;GACR,UAAU;GACV,UAAU;GACV,YAAY;GACb,CACF;EACF;CACF"}
|
package/dist/middleware.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./constants-
|
|
1
|
+
import "./constants-mSHfut_M.mjs";
|
|
2
2
|
import { a as setHandleRoutingKey, o as checkCipherstashAborted, r as setHandleCiphertext, s as raceCipherstashAbort, t as EncryptedString } from "./envelope-P9BxfJNr.mjs";
|
|
3
3
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
4
4
|
//#region src/execution/routing.ts
|
package/dist/pack.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as cipherstashPackMeta } from "./descriptor-meta-
|
|
1
|
+
import { t as cipherstashPackMeta } from "./descriptor-meta-BGF8z8oy.mjs";
|
|
2
2
|
export { cipherstashPackMeta as default };
|
package/dist/runtime.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as CIPHERSTASH_STRING_CODEC_ID, n as CIPHERSTASH_EXTENSION_VERSION, r as CIPHERSTASH_SPACE_ID } from "./constants-
|
|
1
|
+
import { i as CIPHERSTASH_STRING_CODEC_ID, n as CIPHERSTASH_EXTENSION_VERSION, r as CIPHERSTASH_SPACE_ID } from "./constants-mSHfut_M.mjs";
|
|
2
2
|
import { a as setHandleRoutingKey, i as setHandlePlaintextCache, n as isHandleDecrypted, o as checkCipherstashAborted, s as raceCipherstashAbort, t as EncryptedString } from "./envelope-P9BxfJNr.mjs";
|
|
3
3
|
import { CodecImpl } from "@prisma-next/framework-components/codec";
|
|
4
4
|
import { ifDefined } from "@prisma-next/utils/defined";
|
package/package.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/extension-cipherstash",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0-dev.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "CipherStash EQL extension for Prisma Next: contract-space authoring of the encrypted-column scaffolding (eql_v2_configuration table, eql_v2_encrypted/ore_* composite types, eql_v2 domains) plus a baseline migration that installs the vendored EQL bundle SQL byte-for-byte.",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"arktype": "^2.1.29",
|
|
10
|
-
"@prisma-next/
|
|
11
|
-
"@prisma-next/framework-components": "0.
|
|
12
|
-
"@prisma-next/
|
|
13
|
-
"@prisma-next/sql-
|
|
14
|
-
"@prisma-next/
|
|
15
|
-
"@prisma-next/sql-
|
|
16
|
-
"@prisma-next/
|
|
17
|
-
"@prisma-next/
|
|
18
|
-
"@prisma-next/
|
|
19
|
-
"@prisma-next/
|
|
10
|
+
"@prisma-next/contract": "0.6.0-dev.1",
|
|
11
|
+
"@prisma-next/framework-components": "0.6.0-dev.1",
|
|
12
|
+
"@prisma-next/family-sql": "0.6.0-dev.1",
|
|
13
|
+
"@prisma-next/sql-operations": "0.6.0-dev.1",
|
|
14
|
+
"@prisma-next/ts-render": "0.6.0-dev.1",
|
|
15
|
+
"@prisma-next/sql-contract": "0.6.0-dev.1",
|
|
16
|
+
"@prisma-next/sql-relational-core": "0.6.0-dev.1",
|
|
17
|
+
"@prisma-next/utils": "0.6.0-dev.1",
|
|
18
|
+
"@prisma-next/migration-tools": "0.6.0-dev.1",
|
|
19
|
+
"@prisma-next/sql-runtime": "0.6.0-dev.1"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"pathe": "^2.0.3",
|
|
23
23
|
"tsdown": "0.22.0",
|
|
24
24
|
"typescript": "5.9.3",
|
|
25
25
|
"vitest": "4.1.5",
|
|
26
|
-
"@prisma-next/
|
|
27
|
-
"@prisma-next/
|
|
28
|
-
"@prisma-next/
|
|
29
|
-
"@prisma-next/
|
|
30
|
-
"@prisma-next/sql-contract-
|
|
31
|
-
"@prisma-next/
|
|
32
|
-
"@prisma-next/
|
|
26
|
+
"@prisma-next/adapter-postgres": "0.6.0-dev.1",
|
|
27
|
+
"@prisma-next/cli": "0.6.0-dev.1",
|
|
28
|
+
"@prisma-next/psl-parser": "0.6.0-dev.1",
|
|
29
|
+
"@prisma-next/driver-postgres": "0.6.0-dev.1",
|
|
30
|
+
"@prisma-next/sql-contract-psl": "0.6.0-dev.1",
|
|
31
|
+
"@prisma-next/sql-contract-ts": "0.6.0-dev.1",
|
|
32
|
+
"@prisma-next/sql-schema-ir": "0.6.0-dev.1",
|
|
33
|
+
"@prisma-next/target-postgres": "0.6.0-dev.1",
|
|
33
34
|
"@prisma-next/test-utils": "0.0.1",
|
|
34
|
-
"@prisma-next/
|
|
35
|
-
"@prisma-next/tsdown": "0.0.0"
|
|
36
|
-
"@prisma-next/adapter-postgres": "0.5.0-dev.87"
|
|
35
|
+
"@prisma-next/tsconfig": "0.0.0",
|
|
36
|
+
"@prisma-next/tsdown": "0.0.0"
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* (see `../exports/column-types`) to an identical `ColumnTypeDescriptor`
|
|
9
9
|
* so PSL- and TS-authored contracts emit byte-identical `contract.json`.
|
|
10
10
|
*
|
|
11
|
-
* Mirrors `packages/3-extensions/pgvector/src/
|
|
11
|
+
* Mirrors `packages/3-extensions/pgvector/src/core/authoring.ts`. The
|
|
12
12
|
* cipherstash variant differs in three respects:
|
|
13
13
|
* (a) `cipherstash` is the namespace,
|
|
14
14
|
* (b) the constructor takes a single OPTIONAL object argument with two
|
|
@@ -24,10 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import type { AuthoringTypeNamespace } from '@prisma-next/framework-components/authoring';
|
|
27
|
-
import {
|
|
28
|
-
CIPHERSTASH_STRING_CODEC_ID,
|
|
29
|
-
EQL_V2_ENCRYPTED_TYPE,
|
|
30
|
-
} from '../extension-metadata/constants';
|
|
27
|
+
import { CIPHERSTASH_STRING_CODEC_ID, EQL_V2_ENCRYPTED_TYPE } from './extension-metadata/constants';
|
|
31
28
|
|
|
32
29
|
export const cipherstashAuthoringTypes = {
|
|
33
30
|
cipherstash: {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// PSL contract source for the `extension-cipherstash` package.
|
|
2
2
|
//
|
|
3
|
-
// Authored against the
|
|
3
|
+
// Authored against the contract-space package layout convention. The same emit
|
|
4
4
|
// pipeline application authors use is applied here:
|
|
5
5
|
//
|
|
6
|
-
// `prisma-next contract emit` → `<package>/src/contract
|
|
7
|
-
// `prisma-next migration plan` → `<package>/migrations
|
|
6
|
+
// `prisma-next contract emit` → `<package>/src/contract.{json,d.ts}`
|
|
7
|
+
// `prisma-next migration plan` → `<package>/migrations/<dirName>/`
|
|
8
8
|
//
|
|
9
9
|
// The descriptor at `src/exports/control.ts` then wires the emitted JSON
|
|
10
10
|
// artefacts via JSON-import declarations.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* TS contract factory for cipherstash-encrypted string columns.
|
|
3
3
|
*
|
|
4
4
|
* Counterpart to the PSL constructor `cipherstash.EncryptedString({...})`
|
|
5
|
-
* registered in `../contract
|
|
5
|
+
* registered in `../contract-authoring`. Both factories produce the same
|
|
6
6
|
* `ColumnTypeDescriptor` shape so PSL- and TS-authored contracts emit
|
|
7
7
|
* byte-identical `contract.json` (verified by the parity fixture under
|
|
8
8
|
* `test/integration/test/authoring/parity/cipherstash-encrypted-string/`).
|
package/src/exports/control.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Control-plane descriptor for the CipherStash extension.
|
|
3
3
|
*
|
|
4
|
-
* **
|
|
4
|
+
* **Contract-space package layout.** The extension's contract +
|
|
5
5
|
* migrations are emitted by the same pipeline application authors use:
|
|
6
6
|
*
|
|
7
|
-
* `prisma-next contract emit` → `<package>/src/contract
|
|
8
|
-
* `prisma-next migration plan` → `<package>/migrations
|
|
7
|
+
* `prisma-next contract emit` → `<package>/src/contract.{json,d.ts}`
|
|
8
|
+
* `prisma-next migration plan` → `<package>/migrations/<dir>/...`
|
|
9
9
|
*
|
|
10
10
|
* The descriptor wires those JSON artefacts via JSON-import declarations
|
|
11
11
|
* so they flow through the consuming application's module resolver
|
|
12
12
|
* without filesystem assumptions, and synthesises the canonical
|
|
13
|
-
* {@link import('@prisma-next/
|
|
14
|
-
* shape
|
|
15
|
-
* runner / verifier to consume.
|
|
13
|
+
* {@link import('@prisma-next/framework-components/control').MigrationPackage}
|
|
14
|
+
* shape for the framework's runner / verifier to consume.
|
|
16
15
|
*
|
|
17
16
|
* Wired surfaces:
|
|
18
17
|
*
|
|
@@ -25,65 +24,40 @@
|
|
|
25
24
|
* `add_search_config` / `remove_search_config` / rotate behaviour
|
|
26
25
|
* for `searchable: true` `Encrypted<string>` columns.
|
|
27
26
|
*
|
|
28
|
-
* @see docs/architecture docs/adrs/ADR
|
|
29
|
-
* (
|
|
30
|
-
* @see packages/3-extensions/test-contract-space/src/exports/control.ts
|
|
31
|
-
* (reference model).
|
|
27
|
+
* @see docs/architecture docs/adrs/ADR 212 - Contract spaces.md
|
|
28
|
+
* (contract-space package layout convention).
|
|
32
29
|
*/
|
|
33
30
|
|
|
34
|
-
import { fileURLToPath } from 'node:url';
|
|
35
31
|
import type { Contract } from '@prisma-next/contract/types';
|
|
36
32
|
import type { SqlControlExtensionDescriptor } from '@prisma-next/family-sql/control';
|
|
37
|
-
import
|
|
38
|
-
import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
|
|
33
|
+
import { contractSpaceFromJson } from '@prisma-next/migration-tools/spaces';
|
|
39
34
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
40
|
-
import baselineMetadata from '../../migrations/
|
|
35
|
+
import baselineMetadata from '../../migrations/20260601T0000_install_eql_bundle/migration.json' with {
|
|
41
36
|
type: 'json',
|
|
42
37
|
};
|
|
43
|
-
import baselineOps from '../../migrations/
|
|
38
|
+
import baselineOps from '../../migrations/20260601T0000_install_eql_bundle/ops.json' with {
|
|
44
39
|
type: 'json',
|
|
45
40
|
};
|
|
46
|
-
import headRef from '../../migrations/
|
|
47
|
-
import contractJson from '../contract
|
|
41
|
+
import headRef from '../../migrations/refs/head.json' with { type: 'json' };
|
|
42
|
+
import contractJson from '../contract.json' with { type: 'json' };
|
|
48
43
|
import {
|
|
49
44
|
CIPHERSTASH_BASELINE_MIGRATION_NAME,
|
|
50
|
-
CIPHERSTASH_SPACE_ID,
|
|
51
45
|
CIPHERSTASH_STRING_CODEC_ID,
|
|
52
46
|
} from '../extension-metadata/constants';
|
|
53
47
|
import { cipherstashPackMeta } from '../extension-metadata/descriptor-meta';
|
|
54
48
|
import { cipherstashStringCodecHooks } from '../migration/cipherstash-codec';
|
|
55
|
-
import {
|
|
56
|
-
asCipherstashContract,
|
|
57
|
-
asCipherstashMigrationMetadata,
|
|
58
|
-
asCipherstashMigrationOps,
|
|
59
|
-
} from './contract-space-typing';
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Resolve a migration package's on-disk path from this descriptor module's
|
|
63
|
-
* URL. The framework's runner uses `dirPath` for diagnostic messages and
|
|
64
|
-
* to locate sibling files (e.g. `start-contract.json` for non-baseline
|
|
65
|
-
* migrations); pinning it from `import.meta.url` keeps the value correct
|
|
66
|
-
* regardless of where the consuming application installs the package
|
|
67
|
-
* (workspace, node_modules, bundled, etc.).
|
|
68
|
-
*/
|
|
69
|
-
function resolveMigrationDirPath(dirName: string): string {
|
|
70
|
-
return fileURLToPath(
|
|
71
|
-
new URL(`../../migrations/${CIPHERSTASH_SPACE_ID}/${dirName}/`, import.meta.url),
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
49
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
migrations: [baselinePackage],
|
|
50
|
+
const cipherstashContractSpace = contractSpaceFromJson<Contract<SqlStorage>>({
|
|
51
|
+
contractJson,
|
|
52
|
+
migrations: [
|
|
53
|
+
{
|
|
54
|
+
dirName: CIPHERSTASH_BASELINE_MIGRATION_NAME,
|
|
55
|
+
metadata: baselineMetadata,
|
|
56
|
+
ops: baselineOps,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
85
59
|
headRef,
|
|
86
|
-
};
|
|
60
|
+
});
|
|
87
61
|
|
|
88
62
|
const cipherstashExtensionDescriptor: SqlControlExtensionDescriptor<'postgres'> = {
|
|
89
63
|
// Spread pack-meta first so it contributes `kind` / `id` / `familyId`
|
package/src/exports/migration.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* (once published, an invariantId cannot be renamed).
|
|
10
10
|
*
|
|
11
11
|
* The space identifier `'cipherstash'` is what the framework writes to
|
|
12
|
-
* `migrations/cipherstash/`
|
|
12
|
+
* the consuming app's `migrations/cipherstash/` directory and what the marker table's
|
|
13
13
|
* `space` column carries for CipherStash-owned rows.
|
|
14
14
|
*/
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* by the codec lifecycle hook block.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { cipherstashAuthoringTypes } from '../contract
|
|
24
|
+
import { cipherstashAuthoringTypes } from '../contract-authoring';
|
|
25
25
|
import { cipherstashStringCodecMetadata } from './codec-metadata';
|
|
26
26
|
import {
|
|
27
27
|
CIPHERSTASH_EXTENSION_VERSION,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants-B_2TNvUi.mjs","names":[],"sources":["../src/extension-metadata/constants.ts"],"sourcesContent":["/**\n * Static names and identifiers used across CipherStash's contract space.\n *\n * Centralising the strings here so:\n * - the contract IR (`./contract`), the migration ops (`./migrations`),\n * the head ref (`./head-ref`), and the descriptor (`../exports/control`)\n * all reference the same values without typos;\n * - the `cipherstash:*` invariantId namespace is locked in one place\n * (once published, an invariantId cannot be renamed).\n *\n * The space identifier `'cipherstash'` is what the framework writes to\n * `migrations/cipherstash/` in the user's repo and what the marker table's\n * `space` column carries for CipherStash-owned rows.\n */\n\nexport const CIPHERSTASH_SPACE_ID = 'cipherstash';\n\n/**\n * Version advertised by both `cipherstashPackMeta.version` (control plane)\n * and the SDK-bound `SqlRuntimeExtensionDescriptor` (runtime plane).\n *\n * Single source of truth so the descriptor surfaces and the contract-emit\n * pack metadata cannot drift apart; consumed downstream by capability\n * gating and contract round-trips.\n */\nexport const CIPHERSTASH_EXTENSION_VERSION = '0.0.1' as const;\n\n/**\n * Codec id the application-side `Encrypted<string>` lowering targets.\n * Lives here so the codec lifecycle hook (which emits\n * `add_search_config` / `remove_search_config` ops on field events) and\n * the descriptor's `controlPlaneHooks` wiring share the same constant.\n */\nexport const CIPHERSTASH_STRING_CODEC_ID = 'cipherstash/string@1';\n\n/** Schema CipherStash installs its functions/operators/casts/types into. */\nexport const EQL_V2_SCHEMA = 'eql_v2';\n\n/** Configuration table used by EQL's per-column index configuration. */\nexport const EQL_V2_CONFIGURATION_TABLE = 'eql_v2_configuration';\n\n/** Enum type backing the `state` column on `eql_v2_configuration`. */\nexport const EQL_V2_CONFIGURATION_STATE_TYPE = 'eql_v2_configuration_state';\n\n/** JSONB-domain composite type user `Encrypted<string>` columns reference. */\nexport const EQL_V2_ENCRYPTED_TYPE = 'eql_v2_encrypted';\n\n/**\n * Migration directory name for the baseline.\n *\n * Per the framework's per-space layout convention this name is\n * preserved verbatim when the framework writes the package to\n * `migrations/cipherstash/<this-name>/` in the user's repo.\n */\nexport const CIPHERSTASH_BASELINE_MIGRATION_NAME = '20260601T0000_install_eql_bundle';\n\n/**\n * `cipherstash:*` invariantIds emitted by the baseline migration. Each\n * `cipherstash:*` id, once published, is immutable: downstream\n * consumers (other extensions, the marker table) reference them by\n * literal string match.\n *\n * Today the baseline emits a single op (`installBundle`); the bundle\n * SQL is the source of truth for every typed object it creates inside\n * the `eql_v2` schema. New bundle versions or additional structural\n * ops will mint new `cipherstash:*` ids alongside this entry.\n */\nexport const CIPHERSTASH_INVARIANTS = {\n installBundle: 'cipherstash:install-eql-bundle-v1',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAa,uBAAuB;;;;;;;;;AAUpC,MAAa,gCAAgC;;;;;;;AAQ7C,MAAa,8BAA8B;;AAY3C,MAAa,wBAAwB;;;;;;;;AASrC,MAAa,sCAAsC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"descriptor-meta-BgQfZTAF.mjs","names":[],"sources":["../src/contract/authoring.ts","../src/extension-metadata/codec-metadata.ts","../src/extension-metadata/descriptor-meta.ts"],"sourcesContent":["/**\n * Authoring contributions for the cipherstash extension.\n *\n * Registers `cipherstash.EncryptedString({ equality?, freeTextSearch? })`\n * as a namespaced PSL type constructor. The same descriptor lowers a\n * PSL field-type expression like `cipherstash.EncryptedString({ equality:\n * true })` and a TS factory call like `encryptedString({ equality: true })`\n * (see `../exports/column-types`) to an identical `ColumnTypeDescriptor`\n * so PSL- and TS-authored contracts emit byte-identical `contract.json`.\n *\n * Mirrors `packages/3-extensions/pgvector/src/contract/authoring.ts`. The\n * cipherstash variant differs in three respects:\n * (a) `cipherstash` is the namespace,\n * (b) the constructor takes a single OPTIONAL object argument with two\n * optional booleans (so `cipherstash.EncryptedString()`,\n * `cipherstash.EncryptedString({})`, and the fully-spelled\n * `cipherstash.EncryptedString({ equality: true, freeTextSearch: true })`\n * all parse), and\n * (c) both flags default to `true` — searchable encryption is the\n * legitimate default for an extension whose entire reason for\n * existing is to make encrypted columns queryable. Users who want\n * storage-only encryption opt out explicitly:\n * `cipherstash.EncryptedString({ equality: false, freeTextSearch: false })`.\n */\n\nimport type { AuthoringTypeNamespace } from '@prisma-next/framework-components/authoring';\nimport {\n CIPHERSTASH_STRING_CODEC_ID,\n EQL_V2_ENCRYPTED_TYPE,\n} from '../extension-metadata/constants';\n\nexport const cipherstashAuthoringTypes = {\n cipherstash: {\n EncryptedString: {\n kind: 'typeConstructor',\n args: [\n {\n kind: 'object',\n name: 'options',\n optional: true,\n properties: {\n equality: { kind: 'boolean', optional: true },\n freeTextSearch: { kind: 'boolean', optional: true },\n },\n },\n ],\n output: {\n codecId: CIPHERSTASH_STRING_CODEC_ID,\n nativeType: EQL_V2_ENCRYPTED_TYPE,\n typeParams: {\n equality: { kind: 'arg', index: 0, path: ['equality'], default: true },\n freeTextSearch: {\n kind: 'arg',\n index: 0,\n path: ['freeTextSearch'],\n default: true,\n },\n },\n },\n },\n },\n} as const satisfies AuthoringTypeNamespace;\n","/**\n * SDK-free codec used in pack-meta (`cipherstashPackMeta.types.codecTypes\n * .codecInstances`). Pack-meta consumers only read codec *metadata*\n * (`typeId`, `targetTypes`, `traits`, `renderOutputType`) at contract\n * emit time — they never call `encode`/`decode`.\n *\n * The SDK-bound runtime codec for actual `encode`/`decode` lives in\n * `../execution/codec-runtime`; it is resolved through\n * `RuntimeParameterizedCodecDescriptor.factory` at runtime instead of\n * through pack-meta's `codecInstances`.\n *\n * Keeping the SDK-free metadata in its own module — and *not* importing\n * the runtime `CipherstashStringCodec` class — preserves the control\n * vs runtime split. Control-plane consumers (`exports/control.ts`,\n * `exports/pack.ts`) pull this file but never touch the envelope, the\n * SDK interface, or the bulk-encrypt middleware. The bundling-isolation\n * test pins this property by snapshotting that the control entry's\n * chunk graph does not transitively load `envelope-*.mjs`.\n *\n * `encode`/`decode` throw with a clear hint in the misuse case so\n * accidental wiring of the metadata codec into a real runtime path\n * surfaces immediately instead of silently no-op'ing.\n */\n\nimport type { JsonValue } from '@prisma-next/contract/types';\nimport { type AnyCodecDescriptor, CodecImpl } from '@prisma-next/framework-components/codec';\nimport { CIPHERSTASH_STRING_CODEC_ID, EQL_V2_ENCRYPTED_TYPE } from './constants';\n\nconst METADATA_DESCRIPTOR: AnyCodecDescriptor = {\n codecId: CIPHERSTASH_STRING_CODEC_ID,\n traits: [],\n targetTypes: [EQL_V2_ENCRYPTED_TYPE],\n meta: { db: { sql: { postgres: { nativeType: EQL_V2_ENCRYPTED_TYPE } } } },\n paramsSchema: {\n '~standard': {\n version: 1,\n vendor: 'cipherstash',\n validate: (value: unknown) => ({ value }),\n },\n },\n isParameterized: false,\n renderOutputType: () => 'EncryptedString',\n factory: () => () => {\n throw new Error('cipherstash codec: metadata descriptor factory is not callable');\n },\n};\n\nclass CipherstashStringCodecMetadata extends CodecImpl<\n typeof CIPHERSTASH_STRING_CODEC_ID,\n readonly [],\n unknown,\n unknown\n> {\n async encode(): Promise<unknown> {\n throw new Error(\n 'cipherstash codec: encode called on the pack-meta metadata codec. ' +\n 'Construct a runtime descriptor via `createCipherstashRuntimeDescriptor({ sdk })` and use that instead.',\n );\n }\n\n async decode(): Promise<unknown> {\n throw new Error(\n 'cipherstash codec: decode called on the pack-meta metadata codec. ' +\n 'Construct a runtime descriptor via `createCipherstashRuntimeDescriptor({ sdk })` and use that instead.',\n );\n }\n\n encodeJson(): JsonValue {\n return { $encryptedString: '<opaque>' };\n }\n\n decodeJson(): unknown {\n throw new Error(\n 'cipherstash codec: decodeJson is not supported; envelopes do not round-trip through JSON.',\n );\n }\n}\n\nexport const cipherstashStringCodecMetadata = new CipherstashStringCodecMetadata(\n METADATA_DESCRIPTOR,\n);\n","/**\n * Pack metadata for the cipherstash extension.\n *\n * Mirrors `packages/3-extensions/pgvector/src/extension-metadata/descriptor-meta.ts` —\n * the metadata block that gets serialized into `contract.json`'s\n * `extensionPacks.cipherstash` slot at emit time.\n *\n * SDK-free: the runtime descriptor layers SDK-bound codec instances on\n * top at execution time. The `codecInstances` slot here uses the\n * metadata-only\n * codec from `./codec-metadata` because pack-meta consumers only read\n * codec metadata (typeId, targetTypes, traits, renderOutputType);\n * runtime encode/decode always go through the SDK-bound codec produced\n * by `RuntimeParameterizedCodecDescriptor.factory` (see\n * `./parameterized`).\n *\n * The control descriptor in `../exports/control.ts` spreads this pack\n * meta so the framework's contract emitter sees `authoring`,\n * `types.codecTypes.codecInstances`, and `types.storage` alongside\n * the contract-space and codec-lifecycle-hooks blocks already wired\n * by the codec lifecycle hook block.\n */\n\nimport { cipherstashAuthoringTypes } from '../contract/authoring';\nimport { cipherstashStringCodecMetadata } from './codec-metadata';\nimport {\n CIPHERSTASH_EXTENSION_VERSION,\n CIPHERSTASH_SPACE_ID,\n CIPHERSTASH_STRING_CODEC_ID,\n EQL_V2_ENCRYPTED_TYPE,\n} from './constants';\n\nexport { CIPHERSTASH_EXTENSION_VERSION };\n\nexport const cipherstashPackMeta = {\n kind: 'extension',\n id: CIPHERSTASH_SPACE_ID,\n familyId: 'sql',\n targetId: 'postgres',\n version: CIPHERSTASH_EXTENSION_VERSION,\n authoring: {\n type: cipherstashAuthoringTypes,\n },\n types: {\n codecTypes: {\n codecInstances: [cipherstashStringCodecMetadata],\n // `renderOutputType` returns the bare type name `EncryptedString`\n // for parameterized cipherstash columns; the contract emitter\n // needs to import the type alongside that occurrence so the\n // generated `.d.ts` typechecks cleanly. Mirrors pgvector's\n // `Vector` typeImports declaration.\n typeImports: [\n {\n package: '@prisma-next/extension-cipherstash/runtime',\n named: 'EncryptedString',\n alias: 'EncryptedString',\n },\n ],\n },\n queryOperationTypes: {\n import: {\n package: '@prisma-next/extension-cipherstash/operation-types',\n named: 'QueryOperationTypes',\n alias: 'CipherstashQueryOperationTypes',\n },\n },\n storage: [\n {\n typeId: CIPHERSTASH_STRING_CODEC_ID,\n familyId: 'sql',\n targetId: 'postgres',\n nativeType: EQL_V2_ENCRYPTED_TYPE,\n },\n ],\n },\n} as const;\n"],"mappings":";;;AA+BA,MAAa,4BAA4B,EACvC,aAAa,EACX,iBAAiB;CACf,MAAM;CACN,MAAM,CACJ;EACE,MAAM;EACN,MAAM;EACN,UAAU;EACV,YAAY;GACV,UAAU;IAAE,MAAM;IAAW,UAAU;IAAM;GAC7C,gBAAgB;IAAE,MAAM;IAAW,UAAU;IAAM;GACpD;EACF,CACF;CACD,QAAQ;EACN,SAAS;EACT,YAAY;EACZ,YAAY;GACV,UAAU;IAAE,MAAM;IAAO,OAAO;IAAG,MAAM,CAAC,WAAW;IAAE,SAAS;IAAM;GACtE,gBAAgB;IACd,MAAM;IACN,OAAO;IACP,MAAM,CAAC,iBAAiB;IACxB,SAAS;IACV;GACF;EACF;CACF,EACF,EACF;;;ACjCD,MAAM,sBAA0C;CAC9C,SAAS;CACT,QAAQ,EAAE;CACV,aAAa,CAAC,sBAAsB;CACpC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,uBAAuB,EAAE,EAAE,EAAE;CAC1E,cAAc,EACZ,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,WAAoB,EAAE,OAAO;EACzC,EACF;CACD,iBAAiB;CACjB,wBAAwB;CACxB,qBAAqB;EACnB,MAAM,IAAI,MAAM,iEAAiE;;CAEpF;AAED,IAAM,iCAAN,cAA6C,UAK3C;CACA,MAAM,SAA2B;EAC/B,MAAM,IAAI,MACR,2KAED;;CAGH,MAAM,SAA2B;EAC/B,MAAM,IAAI,MACR,2KAED;;CAGH,aAAwB;EACtB,OAAO,EAAE,kBAAkB,YAAY;;CAGzC,aAAsB;EACpB,MAAM,IAAI,MACR,4FACD;;;AAIL,MAAa,iCAAiC,IAAI,+BAChD,oBACD;;;;;;;;;;;;;;;;;;;;;;;;;AC9CD,MAAa,sBAAsB;CACjC,MAAM;CACN,IAAI;CACJ,UAAU;CACV,UAAU;CACV,SAAS;CACT,WAAW,EACT,MAAM,2BACP;CACD,OAAO;EACL,YAAY;GACV,gBAAgB,CAAC,+BAA+B;GAMhD,aAAa,CACX;IACE,SAAS;IACT,OAAO;IACP,OAAO;IACR,CACF;GACF;EACD,qBAAqB,EACnB,QAAQ;GACN,SAAS;GACT,OAAO;GACP,OAAO;GACR,EACF;EACD,SAAS,CACP;GACE,QAAQ;GACR,UAAU;GACV,UAAU;GACV,YAAY;GACb,CACF;EACF;CACF"}
|
|
File without changes
|
|
File without changes
|