export declare const STARTER_ATTACHMENT_SCHEMA = "-- Apply with the migration owner, never from an application process.\nCREATE TABLE public.starter_attachments (\n review_id text PRIMARY KEY REFERENCES public.starter_reviews(id),\n artifact_id uuid NOT NULL UNIQUE, owner_id text NOT NULL, run_id uuid NOT NULL UNIQUE,\n object_path text NOT NULL UNIQUE\n);\nCREATE TABLE public.starter_attachment_results (\n review_id text NOT NULL REFERENCES public.starter_reviews(id),\n version integer NOT NULL CHECK (version >= 2), artifact_id uuid NOT NULL,\n result jsonb NOT NULL, PRIMARY KEY(review_id,version,artifact_id)\n);\nALTER TABLE public.starter_attachments ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.starter_attachment_results ENABLE ROW LEVEL SECURITY;\n"; export declare const STARTER_ATTACHMENT_POSTGRES = "import { SupaCloudArtifactsClient } from \"@supacloud/js\";\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { CommandDatabase, CommandTransaction } from \"@supacloud/db\";\nimport type { ReviewAttachmentInput, ReviewAttachmentResult, ReviewAttachmentStore } from \"../review/attachment\";\n\nexport interface ReviewAttachmentOptions {\n database: CommandDatabase;\n // Provision this client for the same project as the database; never use a caller's bearer token.\n service: SupabaseClient;\n projectId: string;\n tenantId: string;\n}\n\nfunction rows(value: unknown): Record[] {\n if (!Array.isArray(value)) throw new TypeError(\"Invalid attachment database result\");\n return value.map(record);\n}\nfunction isRecord(value: unknown): value is Record {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction record(value: unknown): Record {\n if (!isRecord(value)) throw new TypeError(\"Invalid attachment record\");\n return value;\n}\nfunction durableResult(value: unknown): ReviewAttachmentResult {\n const item = record(value);\n if (typeof item[\"reviewId\"] !== \"string\" || typeof item[\"artifactId\"] !== \"string\"\n || typeof item[\"version\"] !== \"number\" || typeof item[\"sha256\"] !== \"string\"\n || typeof item[\"bytes\"] !== \"number\" || Object.keys(item).length !== 5) {\n throw new Error(\"Invalid durable attachment result\");\n }\n return { reviewId: item[\"reviewId\"], artifactId: item[\"artifactId\"], version: item[\"version\"], sha256: item[\"sha256\"], bytes: item[\"bytes\"] };\n}\n\nexport async function createReviewAttachmentAdapters(options: ReviewAttachmentOptions): Promise<{\n store: ReviewAttachmentStore;\n enqueue(tx: CommandTransaction, reviewId: string, version: number): Promise;\n}> {\n const { database, service, projectId, tenantId } = options;\n if (!projectId.trim() || !tenantId.trim()) throw new TypeError(\"Attachment project/tenant is required\");\n const artifacts = new SupaCloudArtifactsClient(service);\n const binding = rows(await database.transaction(tx => tx.query(\n \"SELECT project_id,tenant_id FROM public.starter_application WHERE singleton\",\n )));\n if (binding.length !== 1 || binding[0]?.[\"project_id\"] !== projectId || binding[0]?.[\"tenant_id\"] !== tenantId) {\n throw new Error(\"Attachment database project/tenant binding mismatch\");\n }\n async function authorize(tx: CommandTransaction, input: ReviewAttachmentInput) {\n const allowed = rows(await tx.query(\n \"SELECT a.object_path,a.run_id FROM public.starter_attachments a \" +\n \"JOIN public.starter_reviews r ON r.id=a.review_id AND r.owner_id=a.owner_id \" +\n \"JOIN public.starter_members m ON m.subject=a.owner_id AND m.enabled AND m.can_approve \" +\n \"CROSS JOIN public.starter_application p \" +\n \"WHERE a.review_id=$1 AND a.artifact_id=$2 AND r.version=$3 AND r.state='approved' \" +\n \"AND p.singleton AND p.project_id=$4 AND p.tenant_id=$5 \" +\n \"FOR UPDATE OF r,a,m FOR SHARE OF p\",\n [input.reviewId, input.artifactId, input.version, projectId, tenantId],\n ));\n if (allowed.length !== 1 || typeof allowed[0]?.[\"object_path\"] !== \"string\") {\n throw new Error(\"Attachment worker authorization or review revision is no longer valid\");\n }\n return allowed[0];\n }\n return {\n async enqueue(tx, reviewId, version) {\n const found = rows(await tx.query(\n \"SELECT artifact_id FROM public.starter_attachments WHERE review_id=$1\", [reviewId],\n ))[0];\n if (!found) return;\n const input = { reviewId, version, artifactId: String(found[\"artifact_id\"]) };\n const allowed = await authorize(tx, input);\n // Bind serialized JSON as text so drivers cannot encode the string as a JSON scalar.\n await tx.query(\"SELECT supacloud_workflows.start_run($1::uuid,$2,'1','verify',$3::text::jsonb,3)\", [\n String(allowed[\"run_id\"]), \"review.verify-attachment\", JSON.stringify(input),\n ]);\n },\n store: {\n async readAttachment(input) {\n const allowed = await database.transaction(tx => authorize(tx, input));\n const artifact = await artifacts.get(input.artifactId);\n if (!artifact || artifact.bucketId !== \"review-attachments\"\n || artifact.objectPath !== allowed[\"object_path\"] || artifact.artifactType !== \"review.attachment\"\n || artifact.mimeType !== \"text/plain\" || !/^[1-9][0-9]*$/.test(artifact.sizeBytes)\n || BigInt(artifact.sizeBytes) > 1048576n) throw new Error(\"Invalid attachment artifact\");\n const downloaded = await service.storage.from(artifact.bucketId).download(artifact.objectPath);\n if (downloaded.error || !downloaded.data) throw new Error(\"Attachment download failed\");\n if (downloaded.data.size !== Number(artifact.sizeBytes)) throw new Error(\"Attachment size mismatch\");\n return { body: new Uint8Array(await downloaded.data.arrayBuffer()), sha256: artifact.sha256 };\n },\n async recordAttachment(input, result) {\n if (result.reviewId !== input.reviewId || result.version !== input.version\n || result.artifactId !== input.artifactId || !/^[a-f0-9]{64}$/.test(result.sha256)\n || !Number.isSafeInteger(result.bytes) || result.bytes < 1 || result.bytes > 1048576) {\n throw new Error(\"Invalid attachment result\");\n }\n return database.transaction(async tx => {\n await authorize(tx, input);\n await tx.query(\n \"INSERT INTO public.starter_attachment_results VALUES($1,$2,$3,$4::text::jsonb) ON CONFLICT DO NOTHING\",\n [input.reviewId, input.version, input.artifactId, JSON.stringify(result)],\n );\n const saved = rows(await tx.query(\n \"SELECT result FROM public.starter_attachment_results WHERE review_id=$1 AND version=$2 AND artifact_id=$3\",\n [input.reviewId, input.version, input.artifactId],\n ))[0]?.[\"result\"];\n const value = durableResult(saved);\n if (value.reviewId !== result.reviewId || value.version !== result.version || value.artifactId !== result.artifactId\n || value.sha256 !== result.sha256 || value.bytes !== result.bytes) {\n throw new Error(\"Conflicting durable attachment result\");\n }\n return value;\n });\n },\n },\n };\n}\n";