/** * Exercises writer identity through generic declarations in both schemas of * the full, type-checked pattern compiler. */ import { expect } from "@std/expect"; import { describe, it } from "@std/testing/bdd"; import type { JSONSchema, JSONSchemaObj } from "@commonfabric/api"; import type { TransformationDiagnostic } from "../src/mod.ts"; import { COMMONFABRIC_TYPES } from "./commonfabric-test-types.ts"; import { callSchemas, parseModule, patternSchemas } from "./transformed-ast.ts"; import { transformSource } from "./utils.ts"; /** Resolves local references and the non-nullish arm of an optional value. */ function valueSchema( schema: JSONSchema | undefined, root: JSONSchemaObj, ): JSONSchemaObj { expect(schema).toBeDefined(); expect(typeof schema).toBe("object"); const object = schema as JSONSchemaObj; if (object.$ref) { const { $ref, ...rest } = object; return { ...valueSchema(root.$defs?.[$ref.split("/").pop()!], root), ...rest, }; } if (object.anyOf) { const value = object.anyOf.filter((arm) => typeof arm === "object" && arm.type !== "undefined" && arm.type !== "null" ); expect(value).toHaveLength(1); return valueSchema(value[0], root); } return object; } describe("generic writer policy", () => { for (const position of ["input", "output"] as const) { it(`keeps writer identities from every merged declaration in the ${position} schema`, async () => { const output = await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); interface Payload { first: T } interface Payload { second: T } type Input = Payload>; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Input>(() => ({} as Input))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true }, ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const root = valueSchema(schema, schema); expect(root.required).toEqual(["first", "second"]); for (const field of ["first", "second"]) { expect(valueSchema(root.properties?.[field], schema)).toMatchObject({ type: "string", ifc: { writeAuthorizedBy: { __ctWriterIdentityOf: { file: "/test.tsx", path: ["f"] }, }, }, }); } }); it(`keeps a named writer policy under Readonly in the ${position} schema`, async () => { const output = await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); type Protected = WriteAuthorizedBy<{ value: string }, typeof f>; type Payload = Readonly; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true }, ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; expect(valueSchema(schema, schema)).toMatchObject({ type: "object", properties: { value: { type: "string" } }, ifc: { writeAuthorizedBy: { __ctWriterIdentityOf: { path: ["f"] } } }, }); }); it(`matches Partial of a value query and its declared shape in the ${position} schema`, async () => { const output = await transformSource( `import { pattern } from "commonfabric"; interface Shape { text: string; value: number; child: { n: number }; values: number[] } declare const x: Shape; type Payload = { queried: Partial; declared: Partial }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true }, ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const root = valueSchema(schema, schema); expect(valueSchema(root.properties?.queried, schema)).toEqual( valueSchema(root.properties?.declared, schema), ); }); it(`matches Partial of an ordinary Owned alias and its declared shape in the ${position} schema`, async () => { const output = await transformSource( `import { pattern } from "commonfabric"; interface Shape { text: string; value: number; child: { n: number }; values: number[] } declare const x: Shape; type Owned = T; type Payload = { named: Partial>; declared: Partial }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true }, ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const root = valueSchema(schema, schema); expect(valueSchema(root.properties?.named, schema)).toEqual( valueSchema(root.properties?.declared, schema), ); }); it(`keeps nongeneric aliases of different instantiations distinct in the ${position} schema`, async () => { const output = await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); const g = handler(() => {}); interface Box { value: T } type F = Box>; type G = Box>; interface Payload { a: F; b: G } export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true }, ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const root = valueSchema(schema, schema); for (const [field, writer] of [["a", "f"], ["b", "g"]]) { const box = valueSchema(root.properties?.[field!], schema); expect( valueSchema(box.properties?.value, schema).ifc?.writeAuthorizedBy, ).toEqual({ __ctWriterIdentityOf: { file: "/test.tsx", path: [writer] }, }); } }); it(`names a recursive generic root at its first reading in the ${position} schema`, async () => { const output = await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); interface Node { value: T; next?: Node } type Payload = Node>; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true }, ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; expect(schema.$ref).toBeDefined(); const root = valueSchema(schema, schema); const next = root.properties?.next as JSONSchemaObj; expect(next.anyOf).toContainEqual({ $ref: schema.$ref }); }); for ( const [name, declaration] of [ [ "a nested literal", "interface Box { inner: { value: T; n: number } }", ], [ "a nested union", "interface Box { inner: { value: T; n: number } | { other: string } }", ], ["an intersection", "type Box = { value: T } & { n: number };"], [ "an array element", "interface Box { list: Array<{ value: T; n: number }> }", ], [ "an optional nested member", "interface Box { inner?: { value?: T; n: number } }", ], ] as const ) { it(`keeps callable member schemas through ${name} in the ${position} schema`, async () => { const diagnostics: TransformationDiagnostic[] = []; const output = await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); const plainFn = () => 1; const subPattern = pattern(() => ({})); ${declaration} interface Guarded { inner: { action: H; value: WriteAuthorizedBy } } type Payload = { handler: Box; plain: Box; subPattern: Box; guarded: Guarded; }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, }, ); expect(diagnostics.filter((item) => item.severity === "error")).toEqual( [], ); const schemas = callSchemas(parseModule(output), "pattern"); expect(schemas).toHaveLength(2); const schema = schemas[position === "input" ? 0 : 1] as JSONSchemaObj; const root = valueSchema(schema, schema); for (const key of ["handler", "plain", "subPattern"]) { let leaf = valueSchema(root.properties?.[key], schema); if (name === "an array element") { leaf = valueSchema( valueSchema(leaf.properties?.list, schema).items, schema, ); } else if (name === "a nested union") { const union = leaf.properties?.inner as JSONSchemaObj; expect(union.anyOf).toHaveLength(2); const arms = union.anyOf!.map((arm) => valueSchema(arm, schema)); expect(arms.filter((arm) => arm.properties?.other)).toEqual([ { type: "object", properties: { other: { type: "string" } }, required: ["other"], }, ]); leaf = arms.find((arm) => arm.properties?.n)!; expect(leaf).toBeDefined(); } else if (name !== "an intersection") { if (name === "an optional nested member") { expect(leaf.required ?? []).not.toContain("inner"); } leaf = valueSchema(leaf.properties?.inner, schema); } expect(leaf.properties).toEqual( key === "handler" ? { value: { asCell: ["stream"] }, n: { type: "number" } } : { n: { type: "number" } }, ); const required = key === "handler" && name !== "an optional nested member" ? ["value", "n"] : ["n"]; expect(leaf.required).toHaveLength(required.length); expect(leaf.required).toEqual(expect.arrayContaining(required)); } const guarded = valueSchema(root.properties?.guarded, schema); const inner = valueSchema(guarded.properties?.inner, schema); expect(inner.properties?.action).toEqual({ asCell: ["stream"] }); expect(valueSchema(inner.properties?.value, schema)).toMatchObject({ type: "string", ifc: { writeAuthorizedBy: { __ctWriterIdentityOf: { path: ["f"] } } }, }); expect(inner.required).toHaveLength(2); expect(inner.required).toEqual( expect.arrayContaining(["action", "value"]), ); }); } for ( const [name, declaration] of [ ["an interface", "interface Box { value: T; n: number }"], ["an object alias", "type Box = { value: T; n: number };"], ] as const ) { it(`keeps callable member schemas through ${name} in the ${position} schema`, async () => { const diagnostics: TransformationDiagnostic[] = []; const output = await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); const plainFn = () => 1; const subPattern = pattern(() => ({})); type H = typeof f; ${declaration} type Guarded = { action: H; value: WriteAuthorizedBy }; type Payload = { handler: Box; aliasedHandler: Box; plain: Box; subPattern: Box; guarded: Guarded; }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, }, ); expect(diagnostics.filter((item) => item.severity === "error")).toEqual( [], ); const schemas = callSchemas(parseModule(output), "pattern"); expect(schemas).toHaveLength(2); const schema = schemas[position === "input" ? 0 : 1] as JSONSchemaObj; const root = valueSchema(schema, schema); for (const name of ["handler", "aliasedHandler"]) { const member = valueSchema(root.properties?.[name], schema); expect(member.properties).toEqual({ value: { asCell: ["stream"] }, n: { type: "number" }, }); expect(member.required).toHaveLength(2); expect(member.required).toEqual( expect.arrayContaining(["value", "n"]), ); } for (const name of ["plain", "subPattern"]) { const member = valueSchema(root.properties?.[name], schema); expect(member.properties).toEqual({ n: { type: "number" } }); expect(member.required).toEqual(["n"]); } const guarded = valueSchema(root.properties?.guarded, schema); expect(guarded.properties?.action).toEqual({ asCell: ["stream"] }); expect(valueSchema(guarded.properties?.value, schema)).toMatchObject({ type: "string", ifc: { writeAuthorizedBy: { __ctWriterIdentityOf: { path: ["f"] } }, }, }); expect(guarded.required).toHaveLength(2); expect(guarded.required).toEqual( expect.arrayContaining(["action", "value"]), ); }); } it(`keeps collapsed writer alternatives through a generic member in the ${position} schema`, async () => { const diagnostics: TransformationDiagnostic[] = []; const output = await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); const g: typeof f = handler(() => {}); interface Box { value: T } type Payload = Box | WriteAuthorizedBy>; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, }, ); expect(diagnostics.filter((item) => item.severity === "error")).toEqual( [], ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const root = valueSchema(schema, schema); const value = root.properties?.value as JSONSchemaObj; expect(value.anyOf).toHaveLength(2); expect( value.anyOf?.map((branch) => { const alternative = valueSchema(branch, schema); expect(alternative.type).toBe("string"); return alternative.ifc?.writeAuthorizedBy; }), ).toEqual([ { __ctWriterIdentityOf: expect.objectContaining({ path: ["f"] }) }, { __ctWriterIdentityOf: expect.objectContaining({ path: ["g"] }) }, ]); }); it(`keeps an optional recursive policy payload's labels on the property in the ${position} schema`, async () => { // The runtime's policy merge refuses a labeled branch beside an // `undefined` branch it cannot prove type-disjoint, as a `$ref` is not. const diagnostics: TransformationDiagnostic[] = []; const output = await transformSource( `import { Confidential, handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); type Identity = X; interface Node { value: W; next?: Sec> } type Sec = Confidential, readonly []>; type Payload = { recursive: Sec> }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, }, ); expect(diagnostics.filter((item) => item.severity === "error")).toEqual( [], ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const recursive = valueSchema( valueSchema(schema, schema).properties?.recursive, schema, ); const next = recursive.properties?.next as JSONSchemaObj; expect(next.anyOf).toBeUndefined(); expect(next.ifc).toEqual({ confidentiality: [] }); expect(recursive.required).toEqual(["value"]); expect(valueSchema(next, schema).properties?.value).toMatchObject({ type: "string", ifc: { writeAuthorizedBy: { __ctWriterIdentityOf: { path: ["f"] } } }, }); }); for ( const [name, declarations, spelling, field] of [ ["a direct member", "", "{ value: POLICY }", "value"], ["an array", "", "Array", "items"], ["a declared tuple", "", "[POLICY]", "items"], [ "a declared index signature", "", "{ [key: string]: POLICY }", "additionalProperties", ], [ "an array alias body", "type Box = Array;", "Box", "items", ], [ "a readonly array alias body", "type Box = ReadonlyArray;", "Box", "items", ], [ "a record alias body", "type Box = Record;", "Box", "additionalProperties", ], ["a tuple alias body", "type Box = [T];", "Box", "items"], [ "a nullable alias body", "type Box = T | null;", "{ value: Box }", "value", ], [ "a class", "class Box { declare value: T }", "Box", "value", ], [ "an inherited class member", "class Base { declare value: T } class Box extends Base {}", "Box", "value", ], [ "a named readonly policy", "type Protected = { value: WriteAuthorizedBy };", "Readonly>", "value", ], [ "an interface", "interface Box { value: W }", "Box", "value", ], [ "an object alias", "type Box = { value: W };", "Box", "value", ], ["a record", "", "Record", "additionalProperties"], [ "a named policy in a record", "type Protected = WriteAuthorizedBy;", "Record>", "additionalProperties", ], [ "an index signature", "interface Box { [key: string]: W }", "Box", "additionalProperties", ], [ "a forwarded alias", "interface Box { value: W } type Forward = Box;", "Forward", "value", ], [ "an inherited member", "interface Base { value: V } interface Box extends Base {}", "Box", "value", ], [ "an indexed payload with a readable writer", 'interface Box { value: WriteAuthorizedBy }', "Box<{ v: string }, WRITER>", "value", ], [ "a CFC alias", 'type Box = Confidential<{ value: W }, readonly ["a"]>;', "Box", "value", ], [ "a recursive interface payload", 'type Identity = X; interface Node { value: W; next?: Sec> } type Sec = Confidential, readonly ["a"]>;', "Sec", "value", ], [ "a recursive type-literal payload", 'type Identity = X; type Sec = Confidential<{ value: W; next?: Sec> }, readonly ["a"]>;', "Sec", "value", ], ] as const ) { it(`keeps distinct writers through ${name} in the pattern's ${position} schema`, async () => { const diagnostics: TransformationDiagnostic[] = []; const source = `import { Confidential, handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); const g: typeof f = handler(() => {}); ${declarations} type Payload = { a: ${ spelling.replace("POLICY", "WriteAuthorizedBy") .replace("WRITER", "typeof f") }; b: ${ spelling.replace("POLICY", "WriteAuthorizedBy") .replace("WRITER", "typeof g") }; }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`; const output = await transformSource(source, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, }); expect(diagnostics.filter((item) => item.severity === "error")).toEqual( [], ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const root = valueSchema(schema, schema); for (const [key, writer] of [["a", "f"], ["b", "g"]] as const) { let container = valueSchema(root.properties?.[key], schema); const depth = name.startsWith("a recursive") ? 4 : 1; for (let level = 0; level < depth; level++) { const target = field === "items" ? container.items : field === "additionalProperties" ? container.additionalProperties : container.properties?.[field]; expect(valueSchema(target as JSONSchema, schema)).toMatchObject({ type: "string", ifc: { writeAuthorizedBy: { __ctWriterIdentityOf: { path: [writer] } }, }, }); if (level + 1 < depth) { container = valueSchema(container.properties?.next, schema); } } } }); } it(`forwards and reverses same-typed writers through a plain alias in the ${position} schema`, async () => { const diagnostics: TransformationDiagnostic[] = []; const source = `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); const g: typeof f = handler(() => {}); type Pair = { left: WriteAuthorizedBy; right: WriteAuthorizedBy }; type Payload = { a: Pair; b: Pair }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`; const output = await transformSource(source, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, }); expect(diagnostics.filter((item) => item.severity === "error")).toEqual( [], ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const root = valueSchema(schema, schema); for ( const [key, left, right] of [["a", "f", "g"], ["b", "g", "f"]] as const ) { const pair = valueSchema(root.properties?.[key], schema); for (const [field, writer] of [["left", left], ["right", right]]) { expect(valueSchema(pair.properties?.[field!], schema)).toMatchObject({ ifc: { writeAuthorizedBy: { __ctWriterIdentityOf: { path: [writer] } }, }, }); } } }); it(`reads writer defaults under preceding parameters in the ${position} schema`, async () => { const diagnostics: TransformationDiagnostic[] = []; const output = await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); const g: typeof f = handler(() => {}); interface Box> { value: W } type Payload = { a: Box; b: Box> }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, }, ); expect(diagnostics.filter((item) => item.severity === "error")).toEqual( [], ); const schema = patternSchemas( parseModule(output), )[position] as JSONSchemaObj; const root = valueSchema(schema, schema); for (const [key, writer] of [["a", "f"], ["b", "g"]] as const) { const box = valueSchema(root.properties?.[key], schema); expect(valueSchema(box.properties?.value, schema)).toMatchObject({ type: "string", ifc: { writeAuthorizedBy: { __ctWriterIdentityOf: { path: [writer] } }, }, }); } }); it(`refuses an unread authored writer through a plain generic in the ${position} schema`, async () => { const diagnostics: TransformationDiagnostic[] = []; await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); type Indirect = typeof f; interface Box { value: W } type Payload = { a: Box> }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, }, ); expect(diagnostics).toContainEqual(expect.objectContaining({ severity: "error", type: "cfc-write-authorized-by:unread", })); }); for ( const [name, declaration, argument] of [ [ "indexed access", 'interface Box { value: W["v"] }', "{ v: WriteAuthorizedBy }", ], [ "a conditional", "interface Box { value: W extends string ? W : never }", "WriteAuthorizedBy", ], [ "an indexed index-signature value", 'interface Box { [key: string]: W["v"] }', "{ v: WriteAuthorizedBy }", ], ] as const ) { it(`refuses a writer whose syntax ${name} discards in the ${position} schema, including stored source`, async () => { for (const storedSource of [false, true]) { const diagnostics: TransformationDiagnostic[] = []; await transformSource( `import { handler, pattern, WriteAuthorizedBy } from "commonfabric"; const f = handler(() => {}); ${declaration} type Payload = { a: Box<${argument}> }; export default ${ position === "input" ? "pattern(() => ({}))" : "pattern<{}, Payload>(() => ({} as Payload))" };`, { types: COMMONFABRIC_TYPES, typeCheck: true, pipelineDiagnostics: diagnostics, storedSource, }, ); expect(diagnostics).toContainEqual(expect.objectContaining({ severity: "error", type: "cfc-write-authorized-by:unread", message: expect.stringContaining("operator syntax"), })); } }); } } });