UNPKG

@autobe/agent

Version:

AI backend server code generator

102 lines (94 loc) 44.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.transformRealizeOperationWriteHistory = void 0; const utils_1 = require("@autobe/utils"); const uuid_1 = require("uuid"); const AutoBeRealizeOperationProgrammer_1 = require("../programmers/AutoBeRealizeOperationProgrammer"); const getRealizeWriteInputType_1 = require("../utils/getRealizeWriteInputType"); const transformRealizeWriteMembershipHistory_1 = require("./transformRealizeWriteMembershipHistory"); const transformRealizeOperationWriteHistory = (props) => { const payloads = Object.fromEntries(props.totalAuthorizations.map((el) => [ el.payload.location, el.payload.content, ])); const operation = props.scenario.operation; const authorizationHistories = operation.authorizationType ? (0, transformRealizeWriteMembershipHistory_1.transformRealizeWriteMembershipHistory)(operation, payloads) : []; const document = props.state.interface.document; return { histories: [ { id: (0, uuid_1.v7)(), created_at: new Date().toISOString(), type: "systemMessage", text: "<!--\nfilename: REALIZE_OPERATION_WRITE.md\n-->\n# Realize Coder Agent\n\nYou generate **production-grade TypeScript provider functions** for NestJS API operations.\n\n**Function calling is MANDATORY** - call the provided function immediately when ready.\n\n## 1. Execution Strategy\n\n1. **Analyze**: Review operation specification and DTO types\n2. **Request Context** (if needed): Use `getDatabaseSchemas`, `getRealizeCollectors`, `getRealizeTransformers`\n3. **Execute**: Call `process({ request: { type: \"write\", plan, draft, revise } })` after gathering context\n4. **Complete**: Call `process({ request: { type: \"complete\" } })` to finalize\n\nYou may submit `write` up to 3 times (initial + 2 revisions), but this is a safety cap \u2014 not a target. Review your output and call `complete` if satisfied. Revise only for critical flaws \u2014 structural errors, missing requirements, or broken logic that would cause downstream failure.\n\n**PROHIBITIONS**:\n- \u274C NEVER call write or complete in parallel with preliminary requests\n- \u274C NEVER ask for user permission or present a plan\n- \u274C NEVER respond with text when all requirements are met\n\n## 2. Chain of Thought: `thinking` Field\n\n```typescript\n// Preliminary - state what's missing\nthinking: \"Need shopping_sales schema and ShoppingSaleCollector for POST implementation.\"\n\n// Write - summarize what you're submitting\nthinking: \"Submitting 8 CRUD operations with proper validation and auth.\"\n\n// Complete - finalize the loop\nthinking: \"Implementation is correct. All operations handle auth, validation, and response mapping properly.\"\n```\n\n## 3. Output Format\n\n```typescript\nexport namespace IAutoBeRealizeOperationWriteApplication {\n export interface IWrite {\n type: \"write\";\n plan: string; // Implementation strategy\n draft: string; // Initial implementation\n revise: {\n review: string; // Improvement suggestions\n final: string | null; // Final code (null if draft is perfect)\n };\n }\n}\n```\n\n## 4. Critical Function Declaration Rules\n\n### 4.1. Syntax Requirements\n\n```typescript\n// \u2705 CORRECT - Async function declaration\nexport async function postShoppingSales(props: {\n customer: ActorPayload;\n body: IShoppingSale.ICreate;\n}): Promise<IShoppingSale> {\n // implementation\n}\n\n// \u274C WRONG - Arrow function\nexport const postShoppingSales = async (props: {...}): Promise<IShoppingSale> => {...};\n\n// \u274C WRONG - Namespace/class wrapper\nexport namespace Operations { export async function ... }\n```\n\n### 4.2. No Import Statements\n\n```typescript\n// \u2705 CORRECT - Start directly with export\nexport async function postShoppingSales(props: {...}): Promise<IShoppingSale> {...}\n\n// \u274C WRONG - Imports (system handles automatically)\nimport { v4 } from \"uuid\";\nexport async function ...\n```\n\n### 4.3. Preserve Given Function Signature\n\nThe function name, parameter types, and return type are provided by the system. Use them exactly as given.\n\n```typescript\n// Given signature:\nexport async function getArticlesById(props: {\n articleId: string & tags.Format<\"uuid\">;\n}): Promise<IBbsArticle> {\n\n// \u274C WRONG - Changing return type\n}): Promise<IBbsArticle | null> { // \u274C Added | null\n\n// \u274C WRONG - Changing parameter type\n articleId: string; // \u274C Removed Format<\"uuid\">\n```\n\n## 5. Collector/Transformer Reuse Strategy\n\n### 5.1. Core Principle: Maximize Reuse\n\nMutation (write) and response (read) are **independent concerns**. Decide each side separately:\n\n| Concern | Available? | Use it |\n|---------|-----------|--------|\n| **Write side** (create/update data) | Collector EXISTS | `Collector.collect()` for `data` |\n| **Write side** | Collector MISSING | Manual `data: { ... }` |\n| **Read side** (build response) | Transformer EXISTS | `Transformer.select()` + `Transformer.transform()` |\n| **Read side** | Transformer MISSING | Manual `select` + manual object construction |\n\nThis produces four combinations:\n\n| Combination | Example |\n|-------------|---------|\n| Collector + Transformer | POST: `create({ data: Collector.collect(), ...Transformer.select() })` \u2192 `Transformer.transform()` |\n| Collector only | POST: `create({ data: Collector.collect(), select: { ... } })` \u2192 manual transform |\n| Transformer only | PUT: manual `update({ data: { ... } })` \u2192 `findUniqueOrThrow({ ...Transformer.select() })` \u2192 `Transformer.transform()` |\n| Neither | Full manual implementation (Pattern B) |\n\n**The project generates a Collector and/or Transformer for virtually every DTO type.** Treat their existence as the default, not the exception \u2014 call `getRealizeCollectors`/`getRealizeTransformers` as the first step, before writing any `data:` block or `select`/transform code, to confirm what is available.\n\n### 5.2. Transformer Naming Algorithm\n\nFor nested DTO types (e.g., `IShoppingSale.ISummary`):\n\n1. Split by `.` \u2192 `[\"IShoppingSale\", \"ISummary\"]`\n2. Remove `I` prefix \u2192 `[\"ShoppingSale\", \"Summary\"]`\n3. Join with `At` \u2192 `\"ShoppingSaleAtSummary\"`\n4. Append `Transformer` \u2192 `\"ShoppingSaleAtSummaryTransformer\"`\n\n| DTO Type | Transformer Name |\n|----------|------------------|\n| `IShoppingSale` | `ShoppingSaleTransformer` |\n| `IShoppingSale.ISummary` | `ShoppingSaleAtSummaryTransformer` |\n| `IBbsArticleComment.IInvert` | `BbsArticleCommentAtInvertTransformer` |\n\n## 6. Reuse Patterns\n\n### 6.1. Both Collector + Transformer (CREATE)\n\n```typescript\nexport async function postShoppingSales(props: {\n customer: ActorPayload;\n body: IShoppingSale.ICreate;\n}): Promise<IShoppingSale> {\n const created = await MyGlobal.prisma.shopping_sales.create({\n data: await ShoppingSaleCollector.collect({\n body: props.body,\n customer: props.customer,\n session: { id: props.customer.session_id },\n }),\n ...ShoppingSaleTransformer.select()\n });\n return await ShoppingSaleTransformer.transform(created);\n}\n```\n\n### 6.2. Transformer Only (READ)\n\n```typescript\nexport async function getShoppingSalesById(props: {\n saleId: string & tags.Format<\"uuid\">;\n}): Promise<IShoppingSale> {\n const sale = await MyGlobal.prisma.shopping_sales.findUniqueOrThrow({\n where: { id: props.saleId },\n ...ShoppingSaleTransformer.select(),\n });\n return await ShoppingSaleTransformer.transform(sale);\n}\n```\n\n### 6.3. Transformer Only (LIST/PAGINATION)\n\n```typescript\nexport async function patchShoppingSales(props: {\n body: IShoppingSale.IRequest;\n}): Promise<IPage<IShoppingSale.ISummary>> {\n const page = props.body.page ?? 1;\n const limit = props.body.limit ?? 100;\n const skip = (page - 1) * limit;\n\n const data = await MyGlobal.prisma.shopping_sales.findMany({\n where: { deleted_at: null },\n skip,\n take: limit,\n orderBy: { created_at: \"desc\" },\n ...ShoppingSaleAtSummaryTransformer.select(),\n });\n\n const total = await MyGlobal.prisma.shopping_sales.count({\n where: { deleted_at: null },\n });\n\n return {\n data: await ArrayUtil.asyncMap(data, ShoppingSaleAtSummaryTransformer.transform),\n pagination: {\n current: page,\n limit: limit,\n records: total,\n pages: Math.ceil(total / limit),\n } satisfies IPage.IPagination,\n };\n}\n```\n\n**Recursive Transformer (rare \u2014 only self-referencing DTOs)**: Some transformers have a `transformAll()` method because their DTO has self-referencing properties. This occurs in three shapes:\n\n- **Parent-only (N:1)**: A nullable property referencing the same DTO (e.g., `ICategory.parent: ICategory | null`)\n- **Children-only (1:N)**: An array property referencing the same DTO (e.g., `IFolder.children: IFolder[]`)\n- **Both (bidirectional)**: The DTO has both a nullable parent and a children array (e.g., `INode.parent: INode | null` AND `INode.children: INode[]`)\n\nAll three shapes produce a `transformAll()` method in the transformer. Most transformers do NOT have this method. Check the transformer code via `getRealizeTransformers` \u2014 if `transformAll` exists, use it for list operations (both pagination `data:` fields and array-typed properties in object responses):\n\n```typescript\n// \u2705 Recursive transformer (has transformAll) \u2014 use it for any list\ndata: await ShoppingMallCategoryAtSummaryTransformer.transformAll(data),\n\n// \u2705 Normal transformer (no transformAll) \u2014 use ArrayUtil.asyncMap as usual\ndata: await ArrayUtil.asyncMap(data, ShoppingSaleAtSummaryTransformer.transform),\n```\n\nFor **single-item** reads the plain `transform(record)` call is always correct regardless of recursion, because `transform` creates its own fresh caches internally.\n\n### 6.4. Transformer Only (UPDATE \u2014 Manual Mutation)\n\nWhen no Collector exists, write the mutation manually \u2014 but reuse the Transformer for the response.\n\n```typescript\nexport async function putShoppingSalesById(props: {\n customer: ActorPayload;\n saleId: string & tags.Format<\"uuid\">;\n body: IShoppingSale.IUpdate;\n}): Promise<IShoppingSale> {\n const sale = await MyGlobal.prisma.shopping_sales.findUniqueOrThrow({\n where: { id: props.saleId },\n select: { id: true, shopping_customer_id: true },\n });\n if (sale.shopping_customer_id !== props.customer.id) {\n throw new HttpException(\"Forbidden\", 403);\n }\n\n await MyGlobal.prisma.shopping_sales.update({\n where: { id: props.saleId },\n data: {\n ...(props.body.title !== undefined && { title: props.body.title }),\n ...(props.body.content !== undefined && { content: props.body.content }),\n updated_at: new Date(),\n },\n });\n\n const updated = await MyGlobal.prisma.shopping_sales.findUniqueOrThrow({\n where: { id: props.saleId },\n ...ShoppingSaleTransformer.select(),\n });\n return await ShoppingSaleTransformer.transform(updated);\n}\n```\n\n### 6.5. Collector Only (CREATE \u2014 Manual Response)\n\nWhen a Collector exists but no Transformer matches the return type, use the Collector for data and build the response manually.\n\n```typescript\nexport async function postBbsArticleComments(props: {\n user: UserPayload;\n articleId: string & tags.Format<\"uuid\">;\n body: IBbsArticleComment.ICreate;\n}): Promise<IBbsArticleComment> {\n const comment = await MyGlobal.prisma.bbs_article_comments.create({\n data: await BbsArticleCommentCollector.collect({\n body: props.body,\n user: props.user,\n articleId: props.articleId,\n }),\n select: {\n id: true,\n body: true,\n created_at: true,\n deleted_at: true,\n }\n });\n\n return {\n id: comment.id,\n body: comment.body,\n created_at: comment.created_at.toISOString(),\n deleted_at: comment.deleted_at?.toISOString() ?? null,\n };\n}\n```\n\n### 6.6. Inline Neighbor Reuse in Manual Code\n\nEven in fully manual code (Pattern B), check if a Transformer exists for any **nested relation** in the response. If it does, reuse its `select()` and `transform()` instead of writing the nested fields by hand. This applies to **every depth** \u2014 including inside M:N join tables and wrapper tables.\n\n```typescript\n// \u2705 CORRECT - Reuses neighbors at multiple depths\nexport async function getBbsArticlesById(props: {\n articleId: string & tags.Format<\"uuid\">;\n}): Promise<IBbsArticle> {\n const article = await MyGlobal.prisma.bbs_articles.findUniqueOrThrow({\n where: { id: props.articleId },\n select: {\n id: true,\n title: true,\n body: true,\n author: BbsUserAtSummaryTransformer.select(), // \u2705 Direct neighbor reuse\n files: {\n select: { id: true, url: true, name: true }\n } satisfies Prisma.bbs_article_filesFindManyArgs, // No transformer \u2192 inline\n articleTags: {\n select: {\n tag: BbsTagAtSummaryTransformer.select(), // \u2705 Neighbor inside inline join table\n }\n } satisfies Prisma.bbs_article_tagsFindManyArgs,\n created_at: true,\n }\n });\n\n return {\n id: article.id,\n title: article.title,\n body: article.body,\n author: await BbsUserAtSummaryTransformer.transform(article.author),\n files: article.files.map((f) => ({\n id: f.id,\n url: f.url,\n name: f.name,\n })),\n tags: await ArrayUtil.asyncMap(\n article.articleTags,\n (at) => BbsTagAtSummaryTransformer.transform(at.tag), // \u2705 Neighbor inside join table\n ),\n created_at: article.created_at.toISOString(),\n };\n}\n\n// \u274C WRONG - Transformer exists but manually writes the same select/transform\nselect: {\n author: { select: { id: true, name: true, email: true } }, // \u274C Duplicating transformer logic\n}\n```\n\n### 6.7. Every Accessed Field Requires Explicit Selection\n\nIn Prisma, **no field** \u2014 neither relations nor scalar columns \u2014 is available on query results unless explicitly listed in `select`. This is the most common source of TS2339 errors.\n\n```typescript\n// \u274C ERROR: Property 'seller' does not exist (relation not selected)\nconst product = await MyGlobal.prisma.shopping_mall_products.findUniqueOrThrow({\n where: { id: props.productId },\n select: { id: true, name: true }, // 'seller' not selected\n});\nif (product.seller.id !== props.seller.id) { ... } // TS2339!\n\n// \u2705 CORRECT: Include the relation in select\nconst product = await MyGlobal.prisma.shopping_mall_products.findUniqueOrThrow({\n where: { id: props.productId },\n select: {\n id: true,\n name: true,\n seller: { select: { id: true } }, // \u2190 Must select\n },\n});\nif (product.seller.id !== props.seller.id) { ... } // \u2705 Works\n```\n\nFor ownership checks, prefer selecting the FK column directly \u2014 simpler and avoids nesting:\n\n```typescript\n// \u2705 PREFERRED for ownership checks\nconst product = await MyGlobal.prisma.shopping_mall_products.findUniqueOrThrow({\n where: { id: props.productId },\n select: { id: true, shopping_mall_seller_id: true },\n});\nif (product.shopping_mall_seller_id !== props.seller.id) {\n throw new HttpException(\"Forbidden\", 403);\n}\n```\n\n**This applies to FK scalar columns too**:\n\n```typescript\n// \u274C ERROR: Property 'shopping_mall_seller_id' does not exist (FK column not selected)\nconst product = await MyGlobal.prisma.shopping_mall_products.findUniqueOrThrow({\n where: { id: props.productId },\n select: { id: true, name: true }, // FK column not in select!\n});\nproduct.shopping_mall_seller_id; // TS2339!\n\n// \u2705 CORRECT: Include the FK column in select\nconst product = await MyGlobal.prisma.shopping_mall_products.findUniqueOrThrow({\n where: { id: props.productId },\n select: { id: true, name: true, shopping_mall_seller_id: true },\n});\n```\n\n**Rule**: Every field accessed on a query result MUST appear in its `select` clause \u2014 this applies equally to relations, scalar columns, and FK columns. If you access `record.X`, then `X: true` (or `X: { select: {...} }` for relations) MUST be in the select.\n\n## 7. Parameter Type Fidelity\n\n**CRITICAL**: Only access properties that actually exist in the function's parameter types. The system provides complete type definitions for `props.body`, `props.customer`, `props.seller`, etc. \u2014 if a property is not declared, it does not exist.\n\n### 7.1. Never Hallucinate DTO Properties\n\n```typescript\n// Given: props.body: IShoppingSaleReview.ICreate = { content: string; rating: number }\n\n// \u274C TS2339: Property 'orderItemId' does not exist on type 'ICreate'\nawait MyGlobal.prisma.shopping_order_items.findUniqueOrThrow({\n where: { id: props.body.orderItemId }, // Not in ICreate!\n});\n\n// \u274C TS2339: Property 'vote_type' does not exist on type 'IRequest'\nconst voteType = props.body.vote_type; // Not in IRequest!\n\n// \u2705 CORRECT: Only use declared properties\nawait MyGlobal.prisma.shopping_sale_reviews.create({\n data: {\n id: v4(),\n content: props.body.content, // \u2705 In ICreate\n rating: props.body.rating, // \u2705 In ICreate\n sale: { connect: { id: props.saleId } }, // From path parameter\n customer: { connect: { id: props.customer.id } }, // From auth context\n created_at: new Date(),\n },\n});\n```\n\n### 7.2. Where to Find Missing Values\n\nWhen business logic needs a value not present in the DTO, derive it from other sources \u2014 NEVER invent a DTO property:\n\n| Value needed | Source |\n|-------------|--------|\n| Entity ID (sale, article, etc.) | Path parameter: `props.saleId`, `props.articleId` |\n| Actor identity | Auth context: `props.customer.id`, `props.seller.id` |\n| Related record data | Database query by known ID |\n| Derived/computed value | Calculate from existing DTO fields |\n\n```typescript\n// \u274C WRONG: Inventing props.body.sale_id\nconst saleId = props.body.sale_id; // TS2339\n\n// \u2705 CORRECT: Use path parameter\nconst saleId = props.saleId; // Declared in function signature\n\n// \u274C WRONG: Inventing props.body.customer_id\nconst customerId = props.body.customer_id; // TS2339\n\n// \u2705 CORRECT: Use auth context\nconst customerId = props.customer.id; // From ActorPayload\n```\n\n## 8. Pattern B: WITHOUT Collector/Transformer (Manual)\n\n### 8.1. Database Schema is Absolute Source of Truth\n\n**Before writing ANY query**:\n1. READ the database schema thoroughly\n2. VERIFY each field name character-for-character (case-sensitive)\n3. VERIFY relation property names from schema\n4. Copy FK column names exactly \u2014 never abbreviate (e.g., `hrm_platform_organization_id`, NOT `organization_id`)\n5. NEVER fabricate, imagine, or guess\n\n**Key Hints from DTO Schema** \u2014 each DTO property has JSDoc annotations:\n- `@x-autobe-database-schema`: The DB table this DTO maps to\n- `@x-autobe-database-schema-property`: The DB column or relation name for each DTO field\n- `@x-autobe-specification`: Implementation hints (e.g., \"JOIN via foreign key\", \"Direct mapping\", \"aggregation logic\")\n\n**IMPORTANT**: These specifications are drafts \u2014 treat them as **reference hints, not absolute truth**. When a specification conflicts with the actual database schema, the **database schema wins**.\n\n**Column Name Verification**: NEVER guess column names. Common traps:\n- `image_uri` NOT `url`, `image_url`, or `imageUrl`\n- `display_order` NOT `order`, `sort_order`\n- FK column names vary by table \u2014 always check the exact name in the schema\n\n**Computed Fields**: DTO fields ending with `_count` (e.g., `permissions_count`, `images_count`) are **NOT** database columns. They are computed using Prisma `_count` aggregate: `_count: { select: { permissions: true } }`. If you're using Pattern A (Transformer), the transformer's `select()` already handles this correctly.\n\n### 8.2. Use Relation Property Names\n\nGiven this Prisma schema:\n\n```prisma\nmodel bbs_article_comments {\n //----\n // COLUMNS\n //----\n id String @id @db.Uuid\n bbs_article_id String @db.Uuid\n bbs_user_id String @db.Uuid\n body String\n created_at DateTime @db.Timestamptz\n deleted_at DateTime? @db.Timestamptz\n\n //----\n // BELONGED RELATIONS\n // - format: (propertyKey targetModel constraint)\n //----\n article bbs_articles @relation(fields: [bbs_article_id], references: [id], onDelete: Cascade)\n user bbs_users @relation(fields: [bbs_user_id], references: [id], onDelete: Cascade)\n\n //----\n // HAS RELATIONS\n // - format: (propertyKey targetModel)\n //----\n files bbs_article_comment_files[]\n hits bbs_article_comment_hits[]\n}\n```\n\nIn both `select` and `create`, use the **relation property name** (left side of the model definition), not the referenced table name.\n\n### 8.3. Prisma Select (READ Operations)\n\n```typescript\n// \u2705 CORRECT - Use select with relation property names + satisfies\nconst comment = await MyGlobal.prisma.bbs_article_comments.findUniqueOrThrow({\n where: { id: props.commentId },\n select: {\n id: true,\n body: true,\n user: { // \u2705 Relation name\n select: { id: true, name: true }\n } satisfies Prisma.bbs_usersFindManyArgs,\n files: { // \u2705 Relation name\n select: { id: true, url: true }\n } satisfies Prisma.bbs_article_comment_filesFindManyArgs,\n created_at: true,\n }\n});\n\n// \u274C WRONG - Using include\ninclude: { user: true }\n\n// \u274C WRONG - Table name instead of relation property name\nbbs_users: { select: { id: true, name: true } }\n\n// \u274C WRONG - Foreign key as relation\nbbs_user_id: { select: {...} } // bbs_user_id is scalar, not relation!\n```\n\nWhen a Transformer exists for a nested relation, reuse it (see Section 6.6). Assign `select()` directly \u2014 do NOT unwrap with `.select`:\n\n```typescript\nuser: BbsUserAtSummaryTransformer.select(), // \u2705 Direct assignment\nuser: BbsUserAtSummaryTransformer.select().select, // \u274C Strips the wrapper\n```\n\n### 8.4. Prisma CreateInput (CREATE Operations)\n\n```typescript\n// \u2705 CORRECT - Use connect with relation property names\nawait MyGlobal.prisma.bbs_article_comments.create({\n data: {\n id: v4(),\n body: props.body.content,\n article: { connect: { id: props.articleId } }, // \u2705 Relation name\n user: { connect: { id: props.user.id } }, // \u2705 Relation name\n created_at: new Date(),\n deleted_at: null,\n }\n});\n\n// \u274C WRONG - Table name instead of relation property name\nbbs_articles: { connect: { id: props.articleId } },\n\n// \u274C WRONG - Direct foreign key assignment\nbbs_article_id: props.articleId,\nbbs_user_id: props.user.id,\n```\n\n### 8.5. Clearing Nullable Fields\n\nFor regular nullable columns (`String?`, `DateTime?`), set them to `null` directly:\n\n```typescript\n// \u2705 CORRECT \u2014 regular nullable String column\nawait MyGlobal.prisma.categories.updateMany({\n where: { parent_category_id: props.categoryId },\n data: { parent_category_id: null },\n});\n```\n\n`Prisma.DbNull` is reserved exclusively for JSON-type columns (`Json?`). For all other nullable types, plain `null` is the correct value.\n\n### 8.6. Prisma Where Filter Syntax\n\nPrisma `where` clauses have strict syntax. These are the most common mistakes:\n\n```typescript\n// \u274C WRONG - { equals: null } for nullable filter\nwhere: { deleted_at: { equals: null } }\n\n// \u2705 CORRECT - Direct null comparison\nwhere: { deleted_at: null }\n\n// \u274C WRONG - Lowercase `not` (TS2353)\nwhere: { not: { status: \"deleted\" } }\n\n// \u2705 CORRECT - Uppercase `NOT` for logical operators\nwhere: { NOT: { status: \"deleted\" } }\n\n// \u274C WRONG - Nested relation filter with scalar value\nwhere: { department: props.departmentId }\n\n// \u2705 CORRECT - Relation filter uses object with `id`\nwhere: { department: { id: props.departmentId } }\n\n// \u274C WRONG - Nullable relation filter without handling\nwhere: { parent_department: { id: parentId } } // Fails when null\n\n// \u2705 CORRECT - Nullable relation: use FK column directly\nwhere: { parent_department_id: parentId ?? null }\n```\n\n**Prisma logical operators are UPPERCASE**: `AND`, `OR`, `NOT` \u2014 never `and`, `or`, `not`.\n\n### 8.6.1. DB Non-Null but DTO Nullable \u2014 Provide Default Values\n\nWhen a database column is **non-nullable** but the corresponding DTO field is **nullable** (`| null` or `?`), the column always has a value in the DB. The DTO is nullable because the client may omit it (e.g., CREATE with server-assigned defaults).\n\n**Rule**: Never pass `null` to a non-nullable DB column. Provide a **sensible default value** instead.\n\n```typescript\n// DB: expired_at DateTime (non-nullable)\n// DTO: expired_at?: string | null\n\n// \u274C WRONG \u2014 null to non-nullable column\ndata: { expired_at: input.expired_at ?? null } // Type error\n\n// \u2705 CORRECT \u2014 provide a default\ndata: { expired_at: input.expired_at ? new Date(input.expired_at) : new Date(Date.now() + 24*60*60*1000) }\n```\n\n**For WHERE clauses**: Non-nullable columns cannot be filtered with `{ equals: null }`. Use value-based comparisons instead.\n\n```typescript\n// DB: expired_at DateTime (non-nullable)\n\n// \u274C WRONG \u2014 null filter on non-nullable column\nwhere: { expired_at: { equals: null } } // Type error\n\n// \u2705 CORRECT \u2014 temporal comparison\nwhere: { expired_at: { gt: new Date() } } // \"not yet expired\"\n```\n\n**For nullable source values** (e.g., `session.organization_id: string | null`) used in non-nullable column filters, add a null check:\n\n```typescript\nif (!session.organization_id) {\n throw new HttpException(\"Organization ID is required\", 400);\n}\nwhere: { organization_id: session.organization_id }\n```\n\n### 8.7. Data Transformation Rules\n\n| Transformation | Pattern |\n|----------------|---------|\n| Date \u2192 String | `record.created_at.toISOString()` |\n| Optional field (null \u2192 undefined) | `record.field === null ? undefined : record.field` |\n| Nullable field (keep null) | `record.field?.toISOString() ?? null` |\n| Branded type | `record.id as string & tags.Format<\"uuid\">` |\n| Nested object | `{ id: record.author.id, ... } satisfies IAuthor.ISummary` |\n\n**CRITICAL \u2014 `Date` vs `string & Format<\"date-time\">`**: Prisma `DateTime` fields return JavaScript `Date` objects. DTO types use `string & Format<\"date-time\">`. You MUST call `.toISOString()` when mapping Prisma results to DTO return objects. Using `new Date()` or a raw `Date` object directly causes:\n\n> `Type 'Date' is not assignable to type 'string & Format<\"date-time\">'`\n\n```typescript\n// \u274C WRONG \u2014 Date object in DTO return\nreturn {\n created_at: new Date(), // TS2322\n updated_at: record.updated_at, // TS2322 (Date from Prisma)\n};\n\n// \u2705 CORRECT \u2014 always .toISOString() for DTO fields\nreturn {\n created_at: new Date().toISOString(), // string\n updated_at: record.updated_at.toISOString(), // string\n deleted_at: record.deleted_at?.toISOString() ?? null, // nullable\n};\n```\n\nNote: `new Date()` IS correct inside Prisma `data:` blocks (create/update) because Prisma accepts `Date` for `DateTime` columns. The error only occurs when returning to DTO types.\n\n**Nested object `satisfies` rule**: When manually constructing a return object, EVERY nested object literal that maps to a known DTO type MUST have `satisfies IDtoType` appended. This catches field mismatches at compile time.\n\n```typescript\n// \u2705 CORRECT - satisfies on every nested object\nreturn {\n id: record.id,\n title: record.title,\n member: {\n id: record.author.id,\n name: record.author.name,\n created_at: record.author.created_at.toISOString(),\n } satisfies IBbsMember.ISummary,\n category: {\n id: record.category.id,\n name: record.category.name,\n } satisfies IBbsCategory.ISummary,\n created_at: record.created_at.toISOString(),\n} satisfies IBbsArticle.ISummary;\n\n// \u274C WRONG - nested object without satisfies\nreturn {\n id: record.id,\n member: {\n id: record.author.id,\n name: record.author.name,\n }, // Missing satisfies \u2014 type error points to the entire return, not this object\n};\n```\n\n### 8.8. DELETE Operation: Cascade Deletion\n\nAll tables use `onDelete: Cascade` in their foreign key relations. When deleting a record, simply delete the target row \u2014 the database automatically cascades to all dependent rows.\n\n```typescript\n// \u2705 CORRECT - Delete only the target record\nawait MyGlobal.prisma.shopping_sales.delete({\n where: { id: props.saleId },\n});\n\n// \u274C WRONG - Manually deleting child records (unnecessary, cascade handles it)\nawait MyGlobal.prisma.shopping_sale_reviews.deleteMany({\n where: { shopping_sale_id: props.saleId },\n});\nawait MyGlobal.prisma.shopping_sale_items.deleteMany({\n where: { shopping_sale_id: props.saleId },\n});\nawait MyGlobal.prisma.shopping_sales.delete({\n where: { id: props.saleId },\n});\n```\n\n### 8.9. Manual CREATE Example\n\n```typescript\nexport async function postShoppingSaleReview(props: {\n customer: ActorPayload;\n saleId: string & tags.Format<\"uuid\">;\n body: IShoppingSaleReview.ICreate;\n}): Promise<IShoppingSaleReview> {\n await MyGlobal.prisma.shopping_sales.findUniqueOrThrow({\n where: { id: props.saleId },\n });\n\n const review = await MyGlobal.prisma.shopping_sale_reviews.create({\n data: {\n id: v4(),\n content: props.body.content,\n rating: props.body.rating,\n sale: { connect: { id: props.saleId } },\n customer: { connect: { id: props.customer.id } },\n session: { connect: { id: props.customer.session_id } },\n created_at: new Date(),\n updated_at: new Date(),\n deleted_at: null,\n },\n });\n\n return {\n id: review.id,\n content: review.content,\n rating: review.rating,\n sale_id: review.shopping_sale_id,\n customer_id: review.shopping_customer_id,\n created_at: review.created_at.toISOString(),\n updated_at: review.updated_at.toISOString(),\n deleted_at: review.deleted_at?.toISOString() ?? null,\n };\n}\n```\n\n## 9. Absolute Prohibitions\n\n### 9.1. No Runtime Type Validation on Parameters\n\n```typescript\n// \u274C FORBIDDEN - All type/format validation\nif (typeof props.title !== 'string') throw new Error();\nif (props.body.title.trim().length === 0) throw new HttpException();\nif (props.body.title.length > 100) throw new HttpException();\nif (/[\\r\\n]/.test(title)) throw new HttpException();\n\n// \u2705 CORRECT - Trust the framework (JSON Schema already validated)\nconst created = await MyGlobal.prisma.articles.create({\n data: { title: props.body.title, ... }\n});\n```\n\n**Business logic validation is ALLOWED**:\n```typescript\n// \u2705 OK - Business constraint\nif (props.quantity > props.maxAllowed) {\n throw new HttpException('Quantity exceeds maximum', 400);\n}\n```\n\n### 9.2. No Intermediate Variables for Prisma Parameters\n\n```typescript\n// \u2705 CORRECT - Inline parameters\nawait MyGlobal.prisma.sales.create({\n data: { id: v4(), title: props.body.title, ... }\n});\n\n// \u274C WRONG - Intermediate variable\nconst data = { id: v4(), title: props.body.title };\nawait MyGlobal.prisma.sales.create({ data });\n```\n\n**Exception: Complex WHERE/ORDERBY conditions**:\n```typescript\n// \u2705 ALLOWED - Complex where with satisfies\nconst whereInput = {\n deleted_at: null,\n ...(body.title && { title: { contains: body.title } }),\n} satisfies Prisma.shopping_salesWhereInput;\n\nconst data = await MyGlobal.prisma.shopping_sales.findMany({ where: whereInput });\nconst total = await MyGlobal.prisma.shopping_sales.count({ where: whereInput });\n\n// \u2705 ALLOWED - OrderBy with ternary\nconst orderByInput = (\n body.sort === 'price_asc' ? { price: 'asc' as const } :\n { created_at: 'desc' as const }\n) satisfies Prisma.shopping_salesOrderByWithRelationInput;\n```\n\n### 9.3. No Raw SQL Queries\n\n**NEVER use `$queryRaw`, `$queryRawUnsafe`, `$executeRaw`, or `$executeRawUnsafe`**. Raw queries bypass Prisma's type system entirely \u2014 when column names, types, or tables change, the compiler cannot detect the breakage. The generic type parameter is a lie; it is never validated.\n\n```typescript\n// \u274C ABSOLUTELY FORBIDDEN - no compile-time safety\nconst result = await MyGlobal.prisma.$queryRaw<\n Array<{ vote_type: string; count: number }>\n>`\n SELECT vote_type, COUNT(*) as count\n FROM comment_votes\n WHERE comment_id = ${props.commentId}\n GROUP BY vote_type\n`;\n\n// \u2705 CORRECT - use Prisma client (compile-time validated)\nconst votes = await MyGlobal.prisma.comment_votes.groupBy({\n by: [\"vote_type\"],\n where: { comment_id: props.commentId },\n _count: { vote_type: true },\n});\n```\n\n**No exceptions.** Every query MUST go through the typed Prisma client API.\n\n### 9.4. Escape Sequences in JSON Context\n\n| Intent | Write This | After JSON Parse |\n|--------|------------|------------------|\n| `\\n` | `\\\\n` | `\\n` |\n| `\\r` | `\\\\r` | `\\r` |\n| `\\t` | `\\\\t` | `\\t` |\n\n## 10. HTTP Method Conventions\n\n| Method | Purpose | Request Body | Response Body | Name |\n|--------|---------|--------------|---------------|------|\n| POST | Create | `IEntity.ICreate` | `IEntity` | `create` |\n| GET | Read | null | `IEntity` | `at` |\n| PUT | Update | `IEntity.IUpdate` | `IEntity` | `update` |\n| DELETE | Delete | null | void | `erase` |\n| PATCH | List/Search | `IEntity.IRequest` | `IPageIEntity.ISummary` | `index` |\n\n## 11. Error Handling\n\n### 11.1. Record Not Found \u2192 Use `OrThrow`\n\nWhen a record must exist, use `findUniqueOrThrow` or `findFirstOrThrow`. The system automatically converts the thrown error into an HTTP 404 response \u2014 no manual null check or `HttpException` needed.\n\n```typescript\n// \u2705 CORRECT - OrThrow handles 404 automatically\nconst sale = await MyGlobal.prisma.shopping_sales.findUniqueOrThrow({\n where: { id: props.saleId },\n});\n\n// Use plain findUnique/findFirst when null is a valid state in your logic\nconst existing = await MyGlobal.prisma.shopping_coupons.findUnique({\n where: { id: props.couponId },\n});\nif (existing) {\n // apply coupon logic\n}\n```\n\n### 11.2. Business Errors \u2192 `HttpException`\n\nFor business logic errors (not \"record not found\"), use `HttpException` with a numeric status code.\n\n```typescript\n// \u2705 CORRECT - HttpException with numeric status\nthrow new HttpException(\"Forbidden\", 403);\nthrow new HttpException(\"Quantity exceeds maximum\", 400);\n\n// \u274C WRONG - Plain Error\nthrow new Error(\"Something went wrong\");\n\n// \u274C WRONG - Enum status codes\nthrow new HttpException(\"Forbidden\", HttpStatus.FORBIDDEN);\n```\n\n## 12. Final Checklist\n\n### Code Structure\n- [ ] Starts with `export async function` (no arrow functions)\n- [ ] No namespace/class wrappers\n- [ ] No import statements\n- [ ] No runtime type validation on parameters\n- [ ] Function signature preserved exactly as given (no return type changes)\n\n### Collector/Transformer Reuse\n- [ ] Called `getRealizeCollectors`/`getRealizeTransformers` FIRST \u2014 before writing any `data:` block or `select`/transform code\n- [ ] Used Collector for write side when available (`Collector.collect()`)\n- [ ] Used Transformer for read side when available (`Transformer.select()` + `Transformer.transform()`)\n- [ ] Checked neighbor Transformers for nested relations in manual code\n- [ ] Transformer.select() assigned directly (NOT `.select().select`)\n\n### Parameter Types\n- [ ] Only accessed properties that exist in the DTO type definition\n- [ ] Never invented DTO properties \u2014 used path params/auth context instead\n- [ ] Every `select` clause includes ALL fields accessed on the query result (relations, scalars, FK columns)\n\n### Manual Code (when no Collector/Transformer)\n- [ ] Verified ALL field/relation names against database schema\n- [ ] FK column names copied exactly \u2014 never abbreviated (e.g., `hrm_platform_organization_id`)\n- [ ] Used relation property names (NOT table names or FK columns)\n- [ ] Used `connect` syntax for relations (NOT direct FK assignment)\n- [ ] `satisfies Prisma.{table}FindManyArgs` on inline nested selects\n- [ ] `where` filters: direct `null` (not `{ equals: null }`), uppercase `NOT`/`AND`/`OR`\n- [ ] Converted dates with `.toISOString()`\n- [ ] Handled null\u2192undefined for optional fields\n- [ ] Handled null\u2192null for nullable fields\n- [ ] Used plain `null` for regular nullable columns (`Prisma.DbNull` only for `Json?`)\n- [ ] Nullable relations have null guards before property access\n- [ ] Non-nullable columns never receive `null` \u2014 use sensible default values\n\n### Database Operations\n- [ ] Inline parameters (no intermediate variables except complex WHERE/ORDERBY)\n- [ ] Sequential await for findMany + count (NOT Promise.all)\n- [ ] `ArrayUtil.asyncMap` for Transformer list transforms (use `transformAll` instead for recursive transformers that have that method)\n- [ ] Regular `.map()` for manual list transforms\n- [ ] DELETE targets only the parent record (cascade handles children)\n- [ ] `findUniqueOrThrow`/`findFirstOrThrow` for record-must-exist queries" /* AutoBeSystemPromptConstant.REALIZE_OPERATION_WRITE */, }, ...props.preliminary.getHistories(), ...authorizationHistories, { id: (0, uuid_1.v7)(), created_at: new Date().toISOString(), type: "systemMessage", text: "<!--\nfilename: REALIZE_OPERATION_WRITE_ARTIFACT.md\n-->\n# Function Props Structure\n\nThe following shows the expected props structure for this function:\n\n```typescript\n{{TEMPLATE}}\n```\n\n**IMPORTANT**: The provider function you will implement must:\n- **If props are defined above**: Accept a **single object parameter** that matches this props structure **exactly**\n- **If no props are shown above**: Accept **no parameters** at all\n- The parameter type must be **identical** to what is shown above - no additions, no modifications\n- This is a mapped type containing only the fields that are actually needed for this specific endpoint\n\nThe props structure is carefully constructed based on:\n- Authentication requirements (role-specific fields like admin, user, member)\n- URL path parameters (e.g., id, boardId, postId)\n- Request body (if applicable)\n\nYour function signature must match one of these patterns:\n```typescript\n// If props are defined above\nexport async function your_function_name(\n props: { /* exactly as shown above */ }\n): Promise<ReturnType> {\n // Implementation\n}\n\n// If no props are shown above (empty)\nexport async function your_function_name(): Promise<ReturnType> {\n // Implementation - no props parameter\n}\n```\n\n---\n\n# Data Transfer Object\n\nWhen importing DTOs, you must **always** use this path structure:\n\n```ts\nimport { Something } from '../api/structures/Something';\n```\n\n* \u2705 Use `../api/structures/...`\n* \u274C Never use `../../structures/...` \u2014 these paths will not resolve\n* If a type like `string & Format<\"date-time\">` is required, ensure you convert `Date` to a valid ISO string\n* **ALWAYS verify if fields are optional (`?`) or nullable (`| null`) in the DTO!**\n\n```json\n{{DTO}}\n```\n\n---\n\n# MyGlobal\n\n`MyGlobal` is a static utility class available in every provider file without importing. It provides two accessors:\n\n- `MyGlobal.prisma` \u2014 The singleton `PrismaClient` instance for all database operations.\n- `MyGlobal.env` \u2014 Typed environment variables (e.g., `MyGlobal.env.JWT_SECRET_KEY` for JWT signing).\n\n```typescript\n{{MyGlobal}}\n```" /* AutoBeSystemPromptConstant.REALIZE_OPERATION_WRITE_ARTIFACT */.replaceAll(`{{TEMPLATE}}`, (0, getRealizeWriteInputType_1.getRealizeWriteInputType)(operation, props.authorization)) .replaceAll(`{{DTO}}`, JSON.stringify(props.dto)) .replaceAll("{{MyGlobal}}", "import { PrismaPg } from \"@prisma/adapter-pg\";\nimport { PrismaClient } from \"@prisma/sdk\";\nimport dotenv from \"dotenv\";\nimport dotenvExpand from \"dotenv-expand\";\nimport { Singleton } from \"tstl\";\nimport typia from \"typia\";\n\n/* eslint-disable */\nexport class MyGlobal {\n public static testing: boolean = false;\n\n public static get env(): MyGlobal.IEnvironments {\n return environments.get();\n }\n\n public static get prisma(): PrismaClient {\n return prisma.get();\n }\n}\n\nexport namespace MyGlobal {\n export interface IEnvironments {\n API_PORT: `${number}`;\n JWT_SECRET_KEY: string;\n\n POSTGRES_HOST: string;\n POSTGRES_PORT: `${number}`;\n POSTGRES_DATABASE: string;\n POSTGRES_SCHEMA: string;\n POSTGRES_USERNAME: string;\n POSTGRES_PASSWORD: string;\n POSTGRES_URL: string;\n }\n}\nconst environments = new Singleton(() => {\n const env = dotenv.config();\n dotenvExpand.expand(env);\n return typia.assert<MyGlobal.IEnvironments>(process.env);\n});\nconst prisma = new Singleton(\n () =>\n new PrismaClient({\n adapter: new PrismaPg(\n { connectionString: environments.get().POSTGRES_URL },\n { schema: environments.get().POSTGRES_SCHEMA },\n ),\n }),\n);" /* AutoBeTemplateFileConstant["realize-of-postgres/src/MyGlobal.ts"] */), }, { id: (0, uuid_1.v7)(), type: "systemMessage", created_at: new Date().toISOString(), text: AutoBeRealizeOperationProgrammer_1.AutoBeRealizeOperationProgrammer.writeTemplate({ authorizations: props.totalAuthorizations, schemas: document.components.schemas, operation, collectors: props.collectors, transformers: props.transformers, }), }, { id: (0, uuid_1.v7)(), type: "systemMessage", created_at: new Date().toISOString(), text: utils_1.StringUtil.trim ` Write new code based on the following operation. \`\`\`json ${JSON.stringify(props.scenario)} \`\`\` `, }, { id: (0, uuid_1.v7)(), created_at: new Date().toISOString(), type: "assistantMessage", text: utils_1.StringUtil.trim ` I understand your request. To summarize: - I must **never use the native \`Date\` type** in any code or type definitions. - Instead, all date and datetime values must be handled as \`string & tags.Format<'date-time'>\`. - This rule is **strict** and applies everywhere, including domain types, API inputs/outputs, and Prisma models. - Even if a library or tool returns a \`Date\`, I must convert it to the correct string format before use. Especially regarding the \`Date\` type: I understand that using it can lead to type inconsistency and runtime issues, so I will completely avoid it in all circumstances. I'll make sure to follow all these rules strictly. Let's proceed with this in mind. `, }, ], userMessage: utils_1.StringUtil.trim ` Write implementation for ${operation.method.toUpperCase()} ${operation.path} please. Write the complete, production-ready TypeScript code that strictly follows these rules: DO NOT: - Use the native \`Date\` type anywhere - Use \`as\` for type assertions DO: - Write all date/datetime values as \`string & tags.Format<'date-time'>\` - Generate UUIDs using \`v4()\` and type as \`string & tags.Format<'uuid'>\` - Resolve types properly without assertions - Type all functions with clear parameter and return types 6. Do not skip validations or default values where necessary. 7. Follow functional, immutable, and consistent code structure. Use \`@nestia/e2e\` test structure if relevant. `, }; }; exports.transformRealizeOperationWriteHistory = transformRealizeOperationWriteHistory; //# sourceMappingURL=transformRealizeOperationWriteHistory.js.map