{"version":3,"file":"spec-utils-Syk4SLCX.mjs","names":[],"sources":["../src/api/common/procedures.ts","../src/api/common/spec-utils.ts"],"sourcesContent":["import { ORMError } from '@zenstackhq/orm';\nimport type { ProcedureDef, ProcedureParam, SchemaDef } from '@zenstackhq/orm/schema';\n\nexport const PROCEDURE_ROUTE_PREFIXES = '$procs' as const;\n\nexport function getProcedureDef(schema: SchemaDef, proc: string): ProcedureDef | undefined {\n    const procs = schema.procedures ?? {};\n    if (!Object.prototype.hasOwnProperty.call(procs, proc)) {\n        return undefined;\n    }\n    return procs[proc];\n}\n\n/**\n * Maps and validates the incoming procedure payload for server-side routing.\n *\n * Supported payload formats:\n * - **Envelope (preferred)**: `{ args: { ... } }`\n * - **Direct object**: `{ ... }` (allowed only when *every* parameter is optional)\n *\n * The function returns the original `payload` unchanged; it only enforces payload\n * *shape* and argument presence/keys so downstream code can safely assume a\n * consistent contract.\n *\n * Validation / branching behavior (mirrors the code below):\n * - **Zero-parameter procedures** (`params.length === 0`)\n *   - `undefined` payload is accepted.\n *   - Object payloads without an `args` key are treated as “no args” and accepted.\n *   - Envelope payloads with `args: {}` are accepted.\n *   - Any other payload (including `args` with keys) is rejected.\n * - **All-optional parameter procedures**\n *   - Payload may be omitted (`undefined`).\n *   - If payload is an object and has no `args` key, it is treated as the direct\n *     object form.\n * - **Missing payload** (required parameters exist)\n *   - `undefined` is rejected.\n * - **Non-object or array payload**\n *   - Rejected.\n * - **Undefined/invalid `args` (envelope form)**\n *   - If `args` is missing and not all params are optional: rejected.\n *   - If `args` exists but is not a non-array object: rejected.\n * - **Unknown keys**\n *   - Any key in the `args` object that is not declared by the procedure is\n *     rejected (prevents silently ignoring typos).\n * - **Missing required params**\n *   - Any declared non-optional param missing from `args` is rejected.\n *\n * Rationale for rejecting null/falsey payloads:\n * - The checks `!payload` and `!argsPayload` intentionally reject values like\n *   `null`, `false`, `0`, or `''` instead of treating them as “no args”. This\n *   keeps the API strictly object-based and yields deterministic, descriptive\n *   errors rather than surprising coercion.\n *\n * @throws {Error} \"procedure does not accept arguments\"\n * @throws {Error} \"missing procedure arguments\"\n * @throws {Error} \"procedure payload must be an object\"\n * @throws {Error} \"procedure `args` must be an object\"\n * @throws {Error} \"unknown procedure argument: <key>\"\n * @throws {Error} \"missing procedure argument: <name>\"\n */\nexport function mapProcedureArgs(procDef: { params: Record<string, ProcedureParam> }, payload: unknown): unknown {\n    const params = Object.values(procDef.params ?? {});\n    if (params.length === 0) {\n        if (typeof payload === 'undefined') {\n            return undefined;\n        }\n        if (payload && typeof payload === 'object' && !Array.isArray(payload)) {\n            const envelope = payload as Record<string, unknown>;\n            const argsPayload = Object.prototype.hasOwnProperty.call(envelope, 'args')\n                ? (envelope as any).args\n                : undefined;\n\n            if (typeof argsPayload === 'undefined') {\n                return payload;\n            }\n\n            if (argsPayload && typeof argsPayload === 'object' && !Array.isArray(argsPayload)) {\n                if (Object.keys(argsPayload as any).length === 0) {\n                    return payload;\n                }\n            }\n        }\n        throw new Error('procedure does not accept arguments');\n    }\n\n    // For procedures where every parameter is optional, allow omitting the payload entirely.\n    if (typeof payload === 'undefined' && params.every((p) => p.optional)) {\n        return undefined;\n    }\n\n    if (typeof payload === 'undefined') {\n        throw new Error('missing procedure arguments');\n    }\n\n    if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n        throw new Error('procedure payload must be an object');\n    }\n\n    const envelope = payload as Record<string, unknown>;\n    const argsPayload = Object.prototype.hasOwnProperty.call(envelope, 'args') ? (envelope as any).args : undefined;\n\n    if (typeof argsPayload === 'undefined') {\n        if (params.every((p) => p.optional)) {\n            return payload;\n        }\n        throw new Error('missing procedure arguments');\n    }\n\n    if (!argsPayload || typeof argsPayload !== 'object' || Array.isArray(argsPayload)) {\n        throw new Error('procedure `args` must be an object');\n    }\n\n    const obj = argsPayload as Record<string, unknown>;\n\n    // reject unknown keys to avoid silently ignoring user mistakes\n    for (const key of Object.keys(obj)) {\n        if (!params.some((p) => p.name === key)) {\n            throw new Error(`unknown procedure argument: ${key}`);\n        }\n    }\n\n    // ensure required params are present\n    for (const p of params) {\n        if (!Object.prototype.hasOwnProperty.call(obj, p.name)) {\n            if (p.optional) {\n                continue;\n            }\n            throw new Error(`missing procedure argument: ${p.name}`);\n        }\n    }\n\n    return payload;\n}\n\nexport function isOrmError(err: unknown): err is ORMError {\n    return err instanceof ORMError;\n}\n","import { lowerCaseFirst } from '@zenstackhq/common-helpers';\nimport type { QueryOptions } from '@zenstackhq/orm';\nimport { ExpressionUtils, type AttributeApplication, type ModelDef, type SchemaDef } from '@zenstackhq/orm/schema';\n\nexport const DEFAULT_SPEC_TITLE = 'ZenStack Generated API';\nexport const DEFAULT_SPEC_VERSION = '1.0.0';\n\n/**\n * Checks if a model is included based on slicing options.\n */\nexport function isModelIncluded(modelName: string, queryOptions?: QueryOptions<any>): boolean {\n    const slicing = queryOptions?.slicing;\n    if (!slicing) return true;\n\n    const excluded = slicing.excludedModels as readonly string[] | undefined;\n    if (excluded?.includes(modelName)) return false;\n\n    const included = slicing.includedModels as readonly string[] | undefined;\n    if (included && !included.includes(modelName)) return false;\n\n    return true;\n}\n\n/**\n * Checks if a CRUD operation is included for a model based on slicing options.\n */\nexport function isOperationIncluded(modelName: string, op: string, queryOptions?: QueryOptions<any>): boolean {\n    const slicing = queryOptions?.slicing;\n    if (!slicing?.models) return true;\n\n    const modelKey = lowerCaseFirst(modelName);\n    const modelSlicing = (slicing.models as Record<string, any>)[modelKey] ?? (slicing.models as any).$all;\n    if (!modelSlicing) return true;\n\n    const excluded = modelSlicing.excludedOperations as readonly string[] | undefined;\n    if (excluded?.includes(op)) return false;\n\n    const included = modelSlicing.includedOperations as readonly string[] | undefined;\n    if (included && !included.includes(op)) return false;\n\n    return true;\n}\n\n/**\n * Checks if a procedure is included based on slicing options.\n */\nexport function isProcedureIncluded(procName: string, queryOptions?: QueryOptions<any>): boolean {\n    const slicing = queryOptions?.slicing;\n    if (!slicing) return true;\n\n    const excluded = slicing.excludedProcedures as readonly string[] | undefined;\n    if (excluded?.includes(procName)) return false;\n\n    const included = slicing.includedProcedures as readonly string[] | undefined;\n    if (included && !included.includes(procName)) return false;\n\n    return true;\n}\n\n/**\n * Checks if a field should be omitted from the output schema based on queryOptions.omit.\n */\nexport function isFieldOmitted(modelName: string, fieldName: string, queryOptions?: QueryOptions<any>): boolean {\n    const omit = queryOptions?.omit as Record<string, Record<string, boolean>> | undefined;\n    return omit?.[modelName]?.[fieldName] === true;\n}\n\n/**\n * Returns the list of model names from the schema that pass the slicing filter.\n */\nexport function getIncludedModels(schema: SchemaDef, queryOptions?: QueryOptions<any>): string[] {\n    return Object.keys(schema.models).filter((name) => isModelIncluded(name, queryOptions));\n}\n\n/**\n * Checks if a filter kind is allowed for a specific field based on slicing options.\n */\nexport function isFilterKindIncluded(\n    modelName: string,\n    fieldName: string,\n    filterKind: string,\n    queryOptions?: QueryOptions<any>,\n): boolean {\n    const slicing = queryOptions?.slicing;\n    if (!slicing?.models) return true;\n\n    const modelKey = lowerCaseFirst(modelName);\n    const modelSlicing = (slicing.models as Record<string, any>)[modelKey] ?? (slicing.models as any).$all;\n    if (!modelSlicing?.fields) return true;\n\n    const fieldSlicing = modelSlicing.fields[fieldName] ?? modelSlicing.fields.$all;\n    if (!fieldSlicing) return true;\n\n    const excluded = fieldSlicing.excludedFilterKinds as readonly string[] | undefined;\n    if (excluded?.includes(filterKind)) return false;\n\n    const included = fieldSlicing.includedFilterKinds as readonly string[] | undefined;\n    if (included && !included.includes(filterKind)) return false;\n\n    return true;\n}\n\n/**\n * Checks if an operation on a model may be denied by access policies.\n * Returns true when `respectAccessPolicies` is enabled and the model's policies\n * for the given operation are NOT a constant allow.\n */\nexport function mayDenyAccess(modelDef: ModelDef, operation: string, respectAccessPolicies?: boolean): boolean {\n    if (!respectAccessPolicies) return false;\n\n    const policyAttrs = (modelDef.attributes ?? []).filter(\n        (attr) => attr.name === '@@allow' || attr.name === '@@deny',\n    );\n\n    // No policy rules at all means default-deny\n    if (policyAttrs.length === 0) return true;\n\n    const getArgByName = (args: AttributeApplication['args'], name: string) =>\n        args?.find((a) => a.name === name)?.value;\n\n    const matchesOperation = (args: AttributeApplication['args']) => {\n        const val = getArgByName(args, 'operation');\n        if (!val || val.kind !== 'literal' || typeof val.value !== 'string') return false;\n        const ops = val.value.split(',').map((s) => s.trim());\n        return ops.includes(operation) || ops.includes('all');\n    };\n\n    const hasEffectiveDeny = policyAttrs.some((attr) => {\n        if (attr.name !== '@@deny' || !matchesOperation(attr.args)) return false;\n        const condition = getArgByName(attr.args, 'condition');\n        // @@deny('op', false) is a no-op — skip it\n        return !(condition?.kind === 'literal' && condition.value === false);\n    });\n    if (hasEffectiveDeny) return true;\n\n    const relevantAllow = policyAttrs.filter((attr) => attr.name === '@@allow' && matchesOperation(attr.args));\n\n    const hasConstantAllow = relevantAllow.some((attr) => {\n        const condition = getArgByName(attr.args, 'condition');\n        return condition?.kind === 'literal' && condition.value === true;\n    });\n\n    return !hasConstantAllow;\n}\n\n/**\n * Extracts a \"description\" from `@@meta(\"description\", \"...\")` or `@meta(\"description\", \"...\")` attributes.\n */\nexport function getMetaDescription(attributes: readonly AttributeApplication[] | undefined): string | undefined {\n    if (!attributes) return undefined;\n    for (const attr of attributes) {\n        if (attr.name !== '@meta' && attr.name !== '@@meta') continue;\n        const nameArg = attr.args?.find((a) => a.name === 'name');\n        if (!nameArg || ExpressionUtils.getLiteralValue(nameArg.value) !== 'description') continue;\n        const valueArg = attr.args?.find((a) => a.name === 'value');\n        if (valueArg) {\n            return ExpressionUtils.getLiteralValue(valueArg.value) as string | undefined;\n        }\n    }\n    return undefined;\n}\n"],"mappings":"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"}