@stonecrop/schema 0.13.0 → 0.13.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 +0 -2
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/index-BdCmYHg0.js +493 -0
- package/dist/index-BdCmYHg0.js.map +1 -0
- package/dist/index-DdC5tpt-.js +488 -0
- package/dist/index-DdC5tpt-.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/schema.d.ts +1 -12
- package/dist/src/cli.d.ts +0 -12
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +1 -0
- package/dist/src/converter/index.d.ts.map +1 -1
- package/dist/src/converter/index.js +1 -6
- package/dist/src/converter/types.d.ts +0 -10
- package/dist/src/converter/types.d.ts.map +1 -1
- package/dist/src/doctype.d.ts +0 -1
- package/dist/src/doctype.d.ts.map +1 -1
- package/dist/src/doctype.js +0 -2
- package/dist/src/fieldtype.d.ts +1 -1
- package/dist/src/fieldtype.d.ts.map +1 -1
- package/dist/src/fieldtype.js +3 -0
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -94,7 +94,6 @@ import { DoctypeMeta } from '@stonecrop/schema'
|
|
|
94
94
|
const doctype: DoctypeMeta = {
|
|
95
95
|
name: 'Sales Order',
|
|
96
96
|
slug: 'sales-order',
|
|
97
|
-
tableName: 'sales_order',
|
|
98
97
|
fields: [
|
|
99
98
|
{
|
|
100
99
|
fieldname: 'customer',
|
|
@@ -389,7 +388,6 @@ const doctypes = convertSchema(ddl, options)
|
|
|
389
388
|
|
|
390
389
|
doctypes.forEach(doctype => {
|
|
391
390
|
console.log(`Doctype: ${doctype.name}`)
|
|
392
|
-
console.log(`Table: ${doctype.tableName}`)
|
|
393
391
|
console.log(`Fields: ${doctype.fields.length}`)
|
|
394
392
|
})
|
|
395
393
|
```
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFileSync as u, existsSync as S, mkdirSync as w, writeFileSync as x
|
|
|
3
3
|
import { resolve as c, join as O } from "node:path";
|
|
4
4
|
import { parseArgs as $ } from "node:util";
|
|
5
5
|
import { getIntrospectionQuery as N } from "graphql";
|
|
6
|
-
import { e as P, v as j } from "./index-
|
|
6
|
+
import { e as P, v as j } from "./index-DdC5tpt-.js";
|
|
7
7
|
async function C(e, m) {
|
|
8
8
|
const t = await fetch(e, {
|
|
9
9
|
method: "POST",
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Stonecrop Schema CLI\n *\n * Converts GraphQL introspection results to Stonecrop doctype JSON schemas.\n *\n * Usage:\n * stonecrop-schema generate --endpoint <url> --output <dir>\n * stonecrop-schema generate --introspection <file.json> --output <dir>\n * stonecrop-schema generate --sdl <file.graphql> --output <dir>\n *\n * @packageDocumentation\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { getIntrospectionQuery, type IntrospectionQuery } from 'graphql'\n\nimport { convertGraphQLSchema } from './converter/index'\nimport { validateDoctype } from './validation'\nimport type { GraphQLConversionOptions } from './converter/types'\n\n/**\n * Fetch an introspection result from a live GraphQL endpoint.\n *\n * @param endpoint - The GraphQL endpoint URL\n * @param headers - Optional HTTP headers\n * @returns The introspection query result\n */\nasync function fetchIntrospection(endpoint: string, headers?: Record<string, string>): Promise<IntrospectionQuery> {\n\tconst response = await fetch(endpoint, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...headers,\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tquery: getIntrospectionQuery(),\n\t\t}),\n\t})\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to fetch introspection: ${response.status} ${response.statusText}`)\n\t}\n\n\tconst json = (await response.json()) as {\n\t\tdata?: IntrospectionQuery\n\t\terrors?: Array<{ message: string }>\n\t}\n\n\tif (json.errors?.length) {\n\t\tthrow new Error(`GraphQL errors: ${json.errors.map(e => e.message).join(', ')}`)\n\t}\n\n\tif (!json.data) {\n\t\tthrow new Error('No data in introspection response')\n\t}\n\n\treturn json.data\n}\n\nasync function main(): Promise<void> {\n\tconst { values, positionals } = parseArgs({\n\t\tallowPositionals: true,\n\t\toptions: {\n\t\t\tendpoint: { type: 'string', short: 'e' },\n\t\t\tintrospection: { type: 'string', short: 'i' },\n\t\t\tsdl: { type: 'string', short: 's' },\n\t\t\toutput: { type: 'string', short: 'o' },\n\t\t\tinclude: { type: 'string' },\n\t\t\texclude: { type: 'string' },\n\t\t\toverrides: { type: 'string' },\n\t\t\t'custom-scalars': { type: 'string' },\n\t\t\t'include-unmapped': { type: 'boolean', default: false },\n\t\t\thelp: { type: 'boolean', short: 'h' },\n\t\t},\n\t})\n\n\tconst command = positionals[0]\n\n\tif (values.help || !command) {\n\t\tprintHelp()\n\t\tprocess.exit(command ? 0 : 1)\n\t}\n\n\tif (command !== 'generate') {\n\t\tconsole.error(`Unknown command: ${command}`)\n\t\tconsole.error('Available commands: generate')\n\t\tprocess.exit(1)\n\t}\n\n\t// Determine source\n\tconst sourceCount = [values.endpoint, values.introspection, values.sdl].filter(Boolean).length\n\tif (sourceCount !== 1) {\n\t\tconsole.error('Exactly one of --endpoint, --introspection, or --sdl must be provided')\n\t\tprocess.exit(1)\n\t}\n\n\tif (!values.output) {\n\t\tconsole.error('--output <dir> is required')\n\t\tprocess.exit(1)\n\t}\n\n\tconst outputDir = resolve(values.output)\n\n\t// Build conversion options\n\tconst options: GraphQLConversionOptions = {\n\t\tincludeUnmappedMeta: values['include-unmapped'],\n\t}\n\n\tif (values.include) {\n\t\toptions.include = values.include.split(',').map(s => s.trim())\n\t}\n\n\tif (values.exclude) {\n\t\toptions.exclude = values.exclude.split(',').map(s => s.trim())\n\t}\n\n\tif (values.overrides) {\n\t\tconst overridesPath = resolve(values.overrides)\n\t\tconst overridesContent = readFileSync(overridesPath, 'utf-8')\n\t\toptions.typeOverrides = JSON.parse(overridesContent)\n\t}\n\n\tif (values['custom-scalars']) {\n\t\tconst scalarsPath = resolve(values['custom-scalars'])\n\t\tconst scalarsContent = readFileSync(scalarsPath, 'utf-8')\n\t\toptions.customScalars = JSON.parse(scalarsContent)\n\t}\n\n\t// Resolve source\n\tlet source: IntrospectionQuery | string\n\n\tif (values.endpoint) {\n\t\tconsole.log(`Fetching introspection from ${values.endpoint}...`)\n\t\tsource = await fetchIntrospection(values.endpoint)\n\t} else if (values.introspection) {\n\t\tconst filePath = resolve(values.introspection)\n\t\tconst content = readFileSync(filePath, 'utf-8')\n\t\tconst parsed = JSON.parse(content)\n\t\t// Handle both { data: { __schema: ... } } and { __schema: ... } formats\n\t\tsource = parsed.data ?? parsed\n\t} else {\n\t\tconst filePath = resolve(values.sdl!)\n\t\tsource = readFileSync(filePath, 'utf-8')\n\t}\n\n\t// Convert\n\tconst doctypes = convertGraphQLSchema(source, options)\n\n\tif (doctypes.length === 0) {\n\t\tconsole.warn('No entity types found in the schema. Check your include/exclude filters.')\n\t\tprocess.exit(0)\n\t}\n\n\t// Write output\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true })\n\t}\n\n\tlet warnings = 0\n\tlet errors = 0\n\n\tfor (const doctype of doctypes) {\n\t\tconst fileName = `${doctype.slug}.json`\n\t\tconst filePath = join(outputDir, fileName)\n\t\tconst json = JSON.stringify(doctype, null, '\\t')\n\n\t\twriteFileSync(filePath, json + '\\n', 'utf-8')\n\n\t\t// Validate the output\n\t\tconst validation = validateDoctype(doctype)\n\t\tif (!validation.success) {\n\t\t\terrors++\n\t\t\tconsole.error(` ERROR: ${fileName} failed validation:`)\n\t\t\tfor (const err of validation.errors) {\n\t\t\t\tconsole.error(` ${err.path.join('.')}: ${err.message}`)\n\t\t\t}\n\t\t} else {\n\t\t\t// Check for unmapped fields\n\t\t\tconst unmappedFields = doctype.fields.filter((f: any) => f._unmapped)\n\t\t\tif (unmappedFields.length > 0) {\n\t\t\t\twarnings++\n\t\t\t\tconsole.warn(\n\t\t\t\t\t` WARN: ${fileName} has ${unmappedFields.length} unmapped field(s): ${unmappedFields\n\t\t\t\t\t\t.map((f: any) => f.fieldname)\n\t\t\t\t\t\t.join(', ')}`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\tconsole.log(\n\t\t`\\nGenerated ${doctypes.length} doctype(s) in ${outputDir}` +\n\t\t\t(warnings ? ` (${warnings} with warnings)` : '') +\n\t\t\t(errors ? ` (${errors} with errors)` : '')\n\t)\n\n\tif (errors > 0) {\n\t\tprocess.exit(1)\n\t}\n}\n\nfunction printHelp(): void {\n\tconsole.log(`\nstonecrop-schema - Convert GraphQL schemas to Stonecrop doctypes\n\nUSAGE:\n stonecrop-schema generate [options]\n\nSOURCE (exactly one required):\n --endpoint, -e <url> Fetch introspection from a live GraphQL endpoint\n --introspection, -i <file> Read from a saved introspection JSON file\n --sdl, -s <file> Read from a GraphQL SDL (.graphql) file\n\nOUTPUT:\n --output, -o <dir> Directory to write doctype JSON files (required)\n\nOPTIONS:\n --include <types> Comma-separated list of type names to include\n --exclude <types> Comma-separated list of type names to exclude\n --overrides <file> JSON file with per-type field overrides\n --custom-scalars <file> JSON file mapping custom scalar names to field templates\n --include-unmapped Include _graphqlType metadata on unmapped fields\n --help, -h Show this help message\n\nEXAMPLES:\n # From a live PostGraphile server\n stonecrop-schema generate -e http://localhost:5000/graphql -o ./schemas\n\n # From a saved introspection result\n stonecrop-schema generate -i introspection.json -o ./schemas\n\n # From an SDL file with custom scalars\n stonecrop-schema generate -s schema.graphql -o ./schemas \\\\\n --custom-scalars custom-scalars.json\n\n # Only convert specific types\n stonecrop-schema generate -e http://localhost:5000/graphql -o ./schemas \\\\\n --include \"User,Post,Comment\"\n`)\n}\n\nmain().catch(err => {\n\tconsole.error('Error:', err.message)\n\tprocess.exit(1)\n})\n"],"names":["fetchIntrospection","endpoint","headers","response","getIntrospectionQuery","json","e","main","values","positionals","parseArgs","command","printHelp","outputDir","resolve","options","s","overridesPath","overridesContent","readFileSync","scalarsPath","scalarsContent","source","filePath","content","parsed","doctypes","convertGraphQLSchema","existsSync","mkdirSync","warnings","errors","doctype","fileName","join","writeFileSync","validation","validateDoctype","unmappedFields","f","err"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/* eslint-disable no-console */\n\n/**\n * Stonecrop Schema CLI\n *\n * Converts GraphQL introspection results to Stonecrop doctype JSON schemas.\n *\n * Usage:\n * stonecrop-schema generate --endpoint <url> --output <dir>\n * stonecrop-schema generate --introspection <file.json> --output <dir>\n * stonecrop-schema generate --sdl <file.graphql> --output <dir>\n *\n * @packageDocumentation\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { getIntrospectionQuery, type IntrospectionQuery } from 'graphql'\n\nimport { convertGraphQLSchema } from './converter/index'\nimport { validateDoctype } from './validation'\nimport type { GraphQLConversionOptions } from './converter/types'\n\n/**\n * Fetch an introspection result from a live GraphQL endpoint.\n *\n * @param endpoint - The GraphQL endpoint URL\n * @param headers - Optional HTTP headers\n * @returns The introspection query result\n */\nasync function fetchIntrospection(endpoint: string, headers?: Record<string, string>): Promise<IntrospectionQuery> {\n\tconst response = await fetch(endpoint, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...headers,\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tquery: getIntrospectionQuery(),\n\t\t}),\n\t})\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to fetch introspection: ${response.status} ${response.statusText}`)\n\t}\n\n\tconst json = (await response.json()) as {\n\t\tdata?: IntrospectionQuery\n\t\terrors?: Array<{ message: string }>\n\t}\n\n\tif (json.errors?.length) {\n\t\tthrow new Error(`GraphQL errors: ${json.errors.map(e => e.message).join(', ')}`)\n\t}\n\n\tif (!json.data) {\n\t\tthrow new Error('No data in introspection response')\n\t}\n\n\treturn json.data\n}\n\nasync function main(): Promise<void> {\n\tconst { values, positionals } = parseArgs({\n\t\tallowPositionals: true,\n\t\toptions: {\n\t\t\tendpoint: { type: 'string', short: 'e' },\n\t\t\tintrospection: { type: 'string', short: 'i' },\n\t\t\tsdl: { type: 'string', short: 's' },\n\t\t\toutput: { type: 'string', short: 'o' },\n\t\t\tinclude: { type: 'string' },\n\t\t\texclude: { type: 'string' },\n\t\t\toverrides: { type: 'string' },\n\t\t\t'custom-scalars': { type: 'string' },\n\t\t\t'include-unmapped': { type: 'boolean', default: false },\n\t\t\thelp: { type: 'boolean', short: 'h' },\n\t\t},\n\t})\n\n\tconst command = positionals[0]\n\n\tif (values.help || !command) {\n\t\tprintHelp()\n\t\tprocess.exit(command ? 0 : 1)\n\t}\n\n\tif (command !== 'generate') {\n\t\tconsole.error(`Unknown command: ${command}`)\n\t\tconsole.error('Available commands: generate')\n\t\tprocess.exit(1)\n\t}\n\n\t// Determine source\n\tconst sourceCount = [values.endpoint, values.introspection, values.sdl].filter(Boolean).length\n\tif (sourceCount !== 1) {\n\t\tconsole.error('Exactly one of --endpoint, --introspection, or --sdl must be provided')\n\t\tprocess.exit(1)\n\t}\n\n\tif (!values.output) {\n\t\tconsole.error('--output <dir> is required')\n\t\tprocess.exit(1)\n\t}\n\n\tconst outputDir = resolve(values.output)\n\n\t// Build conversion options\n\tconst options: GraphQLConversionOptions = {\n\t\tincludeUnmappedMeta: values['include-unmapped'],\n\t}\n\n\tif (values.include) {\n\t\toptions.include = values.include.split(',').map(s => s.trim())\n\t}\n\n\tif (values.exclude) {\n\t\toptions.exclude = values.exclude.split(',').map(s => s.trim())\n\t}\n\n\tif (values.overrides) {\n\t\tconst overridesPath = resolve(values.overrides)\n\t\tconst overridesContent = readFileSync(overridesPath, 'utf-8')\n\t\toptions.typeOverrides = JSON.parse(overridesContent)\n\t}\n\n\tif (values['custom-scalars']) {\n\t\tconst scalarsPath = resolve(values['custom-scalars'])\n\t\tconst scalarsContent = readFileSync(scalarsPath, 'utf-8')\n\t\toptions.customScalars = JSON.parse(scalarsContent)\n\t}\n\n\t// Resolve source\n\tlet source: IntrospectionQuery | string\n\n\tif (values.endpoint) {\n\t\tconsole.log(`Fetching introspection from ${values.endpoint}...`)\n\t\tsource = await fetchIntrospection(values.endpoint)\n\t} else if (values.introspection) {\n\t\tconst filePath = resolve(values.introspection)\n\t\tconst content = readFileSync(filePath, 'utf-8')\n\t\tconst parsed = JSON.parse(content)\n\t\t// Handle both { data: { __schema: ... } } and { __schema: ... } formats\n\t\tsource = parsed.data ?? parsed\n\t} else {\n\t\tconst filePath = resolve(values.sdl!)\n\t\tsource = readFileSync(filePath, 'utf-8')\n\t}\n\n\t// Convert\n\tconst doctypes = convertGraphQLSchema(source, options)\n\n\tif (doctypes.length === 0) {\n\t\tconsole.warn('No entity types found in the schema. Check your include/exclude filters.')\n\t\tprocess.exit(0)\n\t}\n\n\t// Write output\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true })\n\t}\n\n\tlet warnings = 0\n\tlet errors = 0\n\n\tfor (const doctype of doctypes) {\n\t\tconst fileName = `${doctype.slug}.json`\n\t\tconst filePath = join(outputDir, fileName)\n\t\tconst json = JSON.stringify(doctype, null, '\\t')\n\n\t\twriteFileSync(filePath, json + '\\n', 'utf-8')\n\n\t\t// Validate the output\n\t\tconst validation = validateDoctype(doctype)\n\t\tif (!validation.success) {\n\t\t\terrors++\n\t\t\tconsole.error(` ERROR: ${fileName} failed validation:`)\n\t\t\tfor (const err of validation.errors) {\n\t\t\t\tconsole.error(` ${err.path.join('.')}: ${err.message}`)\n\t\t\t}\n\t\t} else {\n\t\t\t// Check for unmapped fields\n\t\t\tconst unmappedFields = doctype.fields.filter((f: any) => f._unmapped)\n\t\t\tif (unmappedFields.length > 0) {\n\t\t\t\twarnings++\n\t\t\t\tconsole.warn(\n\t\t\t\t\t` WARN: ${fileName} has ${unmappedFields.length} unmapped field(s): ${unmappedFields\n\t\t\t\t\t\t.map((f: any) => f.fieldname)\n\t\t\t\t\t\t.join(', ')}`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\tconsole.log(\n\t\t`\\nGenerated ${doctypes.length} doctype(s) in ${outputDir}` +\n\t\t\t(warnings ? ` (${warnings} with warnings)` : '') +\n\t\t\t(errors ? ` (${errors} with errors)` : '')\n\t)\n\n\tif (errors > 0) {\n\t\tprocess.exit(1)\n\t}\n}\n\nfunction printHelp(): void {\n\tconsole.log(`\nstonecrop-schema - Convert GraphQL schemas to Stonecrop doctypes\n\nUSAGE:\n stonecrop-schema generate [options]\n\nSOURCE (exactly one required):\n --endpoint, -e <url> Fetch introspection from a live GraphQL endpoint\n --introspection, -i <file> Read from a saved introspection JSON file\n --sdl, -s <file> Read from a GraphQL SDL (.graphql) file\n\nOUTPUT:\n --output, -o <dir> Directory to write doctype JSON files (required)\n\nOPTIONS:\n --include <types> Comma-separated list of type names to include\n --exclude <types> Comma-separated list of type names to exclude\n --overrides <file> JSON file with per-type field overrides\n --custom-scalars <file> JSON file mapping custom scalar names to field templates\n --include-unmapped Include _graphqlType metadata on unmapped fields\n --help, -h Show this help message\n\nEXAMPLES:\n # From a live PostGraphile server\n stonecrop-schema generate -e http://localhost:5000/graphql -o ./schemas\n\n # From a saved introspection result\n stonecrop-schema generate -i introspection.json -o ./schemas\n\n # From an SDL file with custom scalars\n stonecrop-schema generate -s schema.graphql -o ./schemas \\\\\n --custom-scalars custom-scalars.json\n\n # Only convert specific types\n stonecrop-schema generate -e http://localhost:5000/graphql -o ./schemas \\\\\n --include \"User,Post,Comment\"\n`)\n}\n\nmain().catch(err => {\n\tconsole.error('Error:', err.message)\n\tprocess.exit(1)\n})\n"],"names":["fetchIntrospection","endpoint","headers","response","getIntrospectionQuery","json","e","main","values","positionals","parseArgs","command","printHelp","outputDir","resolve","options","s","overridesPath","overridesContent","readFileSync","scalarsPath","scalarsContent","source","filePath","content","parsed","doctypes","convertGraphQLSchema","existsSync","mkdirSync","warnings","errors","doctype","fileName","join","writeFileSync","validation","validateDoctype","unmappedFields","f","err"],"mappings":"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"}
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { z as e } from "zod";
|
|
2
|
+
import { isScalarType as x, isEnumType as j, isObjectType as T, isNonNullType as L, isListType as U, buildSchema as P, buildClientSchema as q } from "graphql";
|
|
3
|
+
const R = [
|
|
4
|
+
"Data",
|
|
5
|
+
// Short text, varchar
|
|
6
|
+
"Text",
|
|
7
|
+
// Long text
|
|
8
|
+
"Int",
|
|
9
|
+
// Integer
|
|
10
|
+
"Float",
|
|
11
|
+
// Floating point (IEEE 754)
|
|
12
|
+
"Decimal",
|
|
13
|
+
// Arbitrary precision decimal
|
|
14
|
+
"Check",
|
|
15
|
+
// Boolean/checkbox
|
|
16
|
+
"Date",
|
|
17
|
+
// Date only
|
|
18
|
+
"Time",
|
|
19
|
+
// Time only
|
|
20
|
+
"Datetime",
|
|
21
|
+
// Date and time
|
|
22
|
+
"Duration",
|
|
23
|
+
// Time interval
|
|
24
|
+
"DateRange",
|
|
25
|
+
// Date range
|
|
26
|
+
"JSON",
|
|
27
|
+
// JSON data
|
|
28
|
+
"Code",
|
|
29
|
+
// Code/source (with syntax highlighting)
|
|
30
|
+
"Link",
|
|
31
|
+
// Reference to another doctype
|
|
32
|
+
"Attach",
|
|
33
|
+
// File attachment
|
|
34
|
+
"Currency",
|
|
35
|
+
// Currency value
|
|
36
|
+
"Quantity",
|
|
37
|
+
// Quantity with unit
|
|
38
|
+
"Select"
|
|
39
|
+
// Dropdown selection
|
|
40
|
+
], J = e.string().min(1).meta({
|
|
41
|
+
title: "StonecropFieldType",
|
|
42
|
+
description: "Semantic field types for Stonecrop doctypes, consistent across forms and tables"
|
|
43
|
+
});
|
|
44
|
+
function Q(t) {
|
|
45
|
+
return R.includes(t);
|
|
46
|
+
}
|
|
47
|
+
const W = {
|
|
48
|
+
// Text
|
|
49
|
+
Data: { component: "ATextInput", fieldtype: "Data" },
|
|
50
|
+
Text: { component: "ATextInput", fieldtype: "Text" },
|
|
51
|
+
// Numeric
|
|
52
|
+
Int: { component: "ANumericInput", fieldtype: "Int" },
|
|
53
|
+
Float: { component: "ANumericInput", fieldtype: "Float" },
|
|
54
|
+
Decimal: { component: "ADecimalInput", fieldtype: "Decimal" },
|
|
55
|
+
// Boolean
|
|
56
|
+
Check: { component: "ACheckbox", fieldtype: "Check" },
|
|
57
|
+
// Date/Time
|
|
58
|
+
Date: { component: "ADate", fieldtype: "Date" },
|
|
59
|
+
Time: { component: "ATimeInput", fieldtype: "Time" },
|
|
60
|
+
Datetime: { component: "ADatetimePicker", fieldtype: "Datetime" },
|
|
61
|
+
Duration: { component: "ADurationInput", fieldtype: "Duration" },
|
|
62
|
+
DateRange: { component: "ADateRangePicker", fieldtype: "DateRange" },
|
|
63
|
+
// Structured
|
|
64
|
+
JSON: { component: "ACodeEditor", fieldtype: "JSON" },
|
|
65
|
+
Code: { component: "ACodeEditor", fieldtype: "Code" },
|
|
66
|
+
// Relational
|
|
67
|
+
Link: { component: "ALink", fieldtype: "Link" },
|
|
68
|
+
// Files
|
|
69
|
+
Attach: { component: "AFileAttach", fieldtype: "Attach" },
|
|
70
|
+
// Specialized
|
|
71
|
+
Currency: { component: "ACurrencyInput", fieldtype: "Currency" },
|
|
72
|
+
Quantity: { component: "AQuantityInput", fieldtype: "Quantity" },
|
|
73
|
+
Select: { component: "ADropdown", fieldtype: "Select" }
|
|
74
|
+
};
|
|
75
|
+
function z(t) {
|
|
76
|
+
return W[t]?.component ?? "ATextInput";
|
|
77
|
+
}
|
|
78
|
+
function De(t) {
|
|
79
|
+
return Q(t) ? z(t) : "ATextInput";
|
|
80
|
+
}
|
|
81
|
+
const B = e.union([
|
|
82
|
+
e.string(),
|
|
83
|
+
// Link/Doctype target: "customer"
|
|
84
|
+
e.array(e.string()),
|
|
85
|
+
// Select choices: ["A", "B", "C"]
|
|
86
|
+
e.record(e.string(), e.unknown())
|
|
87
|
+
// Config: \{ precision: 10, scale: 2 \}
|
|
88
|
+
]).meta({
|
|
89
|
+
title: "FieldOptions",
|
|
90
|
+
description: "Field options - flexible bag for type-specific configuration"
|
|
91
|
+
}), $ = e.looseObject({
|
|
92
|
+
/** Error message to display when validation fails */
|
|
93
|
+
errorMessage: e.string()
|
|
94
|
+
}).meta({
|
|
95
|
+
title: "FieldValidation",
|
|
96
|
+
description: "Validation configuration for form fields"
|
|
97
|
+
}), k = e.object({
|
|
98
|
+
// === CORE (required) ===
|
|
99
|
+
/** Unique identifier for the field within its doctype */
|
|
100
|
+
fieldname: e.string().min(1),
|
|
101
|
+
/** Semantic field type - determines behavior and default component */
|
|
102
|
+
fieldtype: J,
|
|
103
|
+
// === COMPONENT (optional - derived from fieldtype when not specified) ===
|
|
104
|
+
/** Vue component to render this field. If not specified, derived from TYPE_MAP */
|
|
105
|
+
component: e.string().optional(),
|
|
106
|
+
// === DISPLAY ===
|
|
107
|
+
/** Human-readable label for the field */
|
|
108
|
+
label: e.string().optional(),
|
|
109
|
+
/** Width of the field (CSS value, e.g., "40ch", "200px") */
|
|
110
|
+
width: e.string().optional(),
|
|
111
|
+
/** Text alignment within the field */
|
|
112
|
+
align: e.enum(["left", "center", "right", "start", "end"]).optional(),
|
|
113
|
+
// === BEHAVIOR ===
|
|
114
|
+
/** Whether the field is required */
|
|
115
|
+
required: e.boolean().optional(),
|
|
116
|
+
/** Whether the field is read-only */
|
|
117
|
+
readOnly: e.boolean().optional(),
|
|
118
|
+
/** Whether the field is editable (for table cells) */
|
|
119
|
+
edit: e.boolean().optional(),
|
|
120
|
+
/** Whether the field is hidden from the UI */
|
|
121
|
+
hidden: e.boolean().optional(),
|
|
122
|
+
// === VALUE ===
|
|
123
|
+
/** Current value of the field */
|
|
124
|
+
value: e.unknown().optional(),
|
|
125
|
+
/** Default value for new records */
|
|
126
|
+
default: e.unknown().optional(),
|
|
127
|
+
// === TYPE-SPECIFIC ===
|
|
128
|
+
/**
|
|
129
|
+
* Type-specific options:
|
|
130
|
+
* - Link: target doctype slug ("customer")
|
|
131
|
+
* - Doctype: child doctype slug ("sales-order-item")
|
|
132
|
+
* - Select: choices array (["Draft", "Submitted"])
|
|
133
|
+
* - Decimal: \{ precision, scale \}
|
|
134
|
+
* - Code: \{ language \}
|
|
135
|
+
*/
|
|
136
|
+
options: B.optional(),
|
|
137
|
+
/**
|
|
138
|
+
* Cardinality for Doctype fields:
|
|
139
|
+
* - 'one': exactly 1 (default)
|
|
140
|
+
* - 'atMostOne': 0 or 1
|
|
141
|
+
* - 'noneOrMany': 0 or more
|
|
142
|
+
* - 'atLeastOne': 1 or more
|
|
143
|
+
*/
|
|
144
|
+
cardinality: e.enum(["one", "atMostOne", "noneOrMany", "atLeastOne"]).optional(),
|
|
145
|
+
/**
|
|
146
|
+
* Input mask pattern. Accepts either a plain mask string or a stringified
|
|
147
|
+
* arrow function that receives `locale` and returns a mask string.
|
|
148
|
+
*
|
|
149
|
+
* Plain pattern: `"##/##/####"`
|
|
150
|
+
*
|
|
151
|
+
* Function pattern: `"(locale) => locale === 'en-US' ? '(###) ###-####' : '####-######'"`
|
|
152
|
+
*/
|
|
153
|
+
mask: e.string().optional(),
|
|
154
|
+
// === VALIDATION ===
|
|
155
|
+
/** Validation configuration */
|
|
156
|
+
validation: $.optional()
|
|
157
|
+
}).meta({
|
|
158
|
+
title: "FieldMeta",
|
|
159
|
+
description: "Unified field metadata - the single source of truth for field definitions, works for both forms (AForm) and tables (ATable)"
|
|
160
|
+
}), G = e.enum(["atMostOne", "one", "noneOrMany", "atLeastOne"]).meta({
|
|
161
|
+
title: "Cardinality",
|
|
162
|
+
description: "Cardinality for relationship links between doctypes"
|
|
163
|
+
}), V = e.object({
|
|
164
|
+
/** Fetch method type */
|
|
165
|
+
method: e.literal("sync"),
|
|
166
|
+
/** Optional limit on number of records to fetch */
|
|
167
|
+
limit: e.number().int().positive().optional()
|
|
168
|
+
}).meta({
|
|
169
|
+
title: "SyncFetch",
|
|
170
|
+
description: "Sync fetch strategy - data is fetched in the initial query"
|
|
171
|
+
}), Y = e.object({
|
|
172
|
+
/** Fetch method type */
|
|
173
|
+
method: e.literal("lazy")
|
|
174
|
+
}).meta({
|
|
175
|
+
title: "LazyFetch",
|
|
176
|
+
description: "Lazy fetch strategy - data is fetched on demand in a separate query"
|
|
177
|
+
}), Z = e.object({
|
|
178
|
+
/** Fetch method type */
|
|
179
|
+
method: e.literal("custom"),
|
|
180
|
+
/** Serialized handler function to invoke */
|
|
181
|
+
handler: e.string()
|
|
182
|
+
}).meta({
|
|
183
|
+
title: "CustomFetch",
|
|
184
|
+
description: "Custom fetch strategy - uses a custom handler function"
|
|
185
|
+
}), K = e.discriminatedUnion("method", [V, Y, Z]).meta({
|
|
186
|
+
title: "FetchStrategy",
|
|
187
|
+
description: "Fetch strategy for link data loading"
|
|
188
|
+
}), H = e.object({
|
|
189
|
+
/** Target doctype slug */
|
|
190
|
+
target: e.string().min(1),
|
|
191
|
+
/** Cardinality of the relationship */
|
|
192
|
+
cardinality: G,
|
|
193
|
+
/** Backlink fieldname on the target doctype that points back to this link */
|
|
194
|
+
backlink: e.string().optional(),
|
|
195
|
+
/** Override default rendering component (AForm for 1:1, ATable for 1:many) */
|
|
196
|
+
component: e.string().optional(),
|
|
197
|
+
/** Fieldname of the corresponding Link field in the fields array */
|
|
198
|
+
fieldname: e.string().min(1).optional(),
|
|
199
|
+
/** Fetch strategy for loading nested data */
|
|
200
|
+
fetch: K.optional(),
|
|
201
|
+
/** Whether to block workflow actions until nested data is loaded (default: true) */
|
|
202
|
+
blockWorkflows: e.boolean().optional()
|
|
203
|
+
}).meta({
|
|
204
|
+
title: "LinkDeclaration",
|
|
205
|
+
description: "Declares a relationship from one doctype to another"
|
|
206
|
+
}), X = e.object({
|
|
207
|
+
/** Display label for the action */
|
|
208
|
+
label: e.string().min(1),
|
|
209
|
+
/** Handler function name or path */
|
|
210
|
+
handler: e.string().min(1),
|
|
211
|
+
/** Fields that must have values before action can execute */
|
|
212
|
+
requiredFields: e.array(e.string()).optional(),
|
|
213
|
+
/** Workflow states where this action is available */
|
|
214
|
+
allowedStates: e.array(e.string()).optional(),
|
|
215
|
+
/** Whether to show a confirmation dialog */
|
|
216
|
+
confirm: e.boolean().optional(),
|
|
217
|
+
/** Additional arguments for the action */
|
|
218
|
+
args: e.record(e.string(), e.unknown()).optional()
|
|
219
|
+
}).meta({
|
|
220
|
+
title: "ActionDefinition",
|
|
221
|
+
description: "Action definition within a workflow"
|
|
222
|
+
}), ee = e.object({
|
|
223
|
+
/** List of workflow states */
|
|
224
|
+
states: e.array(e.string()).optional(),
|
|
225
|
+
/** Actions available in this workflow */
|
|
226
|
+
actions: e.record(e.string(), X).optional()
|
|
227
|
+
}).meta({
|
|
228
|
+
title: "WorkflowMeta",
|
|
229
|
+
description: "Workflow metadata - states and actions for a doctype"
|
|
230
|
+
}), _ = e.object({
|
|
231
|
+
/** Display name of the doctype */
|
|
232
|
+
name: e.string().min(1),
|
|
233
|
+
/** URL-friendly slug (kebab-case) */
|
|
234
|
+
slug: e.string().min(1).optional(),
|
|
235
|
+
/** Database table name */
|
|
236
|
+
tableName: e.string().optional(),
|
|
237
|
+
/** Field definitions (including link fields with fieldtype: 'Link') */
|
|
238
|
+
fields: e.array(k),
|
|
239
|
+
/** Relationship links to other doctypes */
|
|
240
|
+
links: e.record(e.string(), H).optional(),
|
|
241
|
+
/** Workflow configuration */
|
|
242
|
+
workflow: ee.optional(),
|
|
243
|
+
/** Parent doctype for inheritance */
|
|
244
|
+
inherits: e.string().optional()
|
|
245
|
+
}).meta({
|
|
246
|
+
title: "DoctypeMeta",
|
|
247
|
+
description: "Doctype metadata - complete definition of a doctype"
|
|
248
|
+
});
|
|
249
|
+
function Se(t) {
|
|
250
|
+
const n = k.safeParse(t);
|
|
251
|
+
return n.success ? { success: !0, errors: [] } : {
|
|
252
|
+
success: !1,
|
|
253
|
+
errors: n.error.issues.map((o) => ({
|
|
254
|
+
path: o.path,
|
|
255
|
+
message: o.message
|
|
256
|
+
}))
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function be(t) {
|
|
260
|
+
const n = _.safeParse(t);
|
|
261
|
+
return n.success ? { success: !0, errors: [] } : {
|
|
262
|
+
success: !1,
|
|
263
|
+
errors: n.error.issues.map((o) => ({
|
|
264
|
+
path: o.path,
|
|
265
|
+
message: o.message
|
|
266
|
+
}))
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function ke(t) {
|
|
270
|
+
return k.parse(t);
|
|
271
|
+
}
|
|
272
|
+
function Ie(t) {
|
|
273
|
+
return _.parse(t);
|
|
274
|
+
}
|
|
275
|
+
function Fe(t) {
|
|
276
|
+
return t.replace(/_([a-z])/g, (n, o) => o.toUpperCase());
|
|
277
|
+
}
|
|
278
|
+
function Ce(t) {
|
|
279
|
+
return t.replace(/[A-Z]/g, (n) => `_${n.toLowerCase()}`);
|
|
280
|
+
}
|
|
281
|
+
function Le(t) {
|
|
282
|
+
return t.split("_").map((n) => n.charAt(0).toUpperCase() + n.slice(1).toLowerCase()).join(" ");
|
|
283
|
+
}
|
|
284
|
+
function te(t) {
|
|
285
|
+
const n = t.replace(/([A-Z])/g, " $1").trim();
|
|
286
|
+
return n.charAt(0).toUpperCase() + n.slice(1);
|
|
287
|
+
}
|
|
288
|
+
function ne(t) {
|
|
289
|
+
return t.split(/[-_\s]+/).map((n) => n.charAt(0).toUpperCase() + n.slice(1).toLowerCase()).join("");
|
|
290
|
+
}
|
|
291
|
+
function h(t) {
|
|
292
|
+
return t.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
293
|
+
}
|
|
294
|
+
function ie(t) {
|
|
295
|
+
return t.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
|
|
296
|
+
}
|
|
297
|
+
const oe = {
|
|
298
|
+
String: { component: "ATextInput", fieldtype: "Data" },
|
|
299
|
+
Int: { component: "ANumericInput", fieldtype: "Int" },
|
|
300
|
+
Float: { component: "ANumericInput", fieldtype: "Float" },
|
|
301
|
+
Boolean: { component: "ACheckbox", fieldtype: "Check" },
|
|
302
|
+
ID: { component: "ATextInput", fieldtype: "Data" }
|
|
303
|
+
}, ae = {
|
|
304
|
+
// Arbitrary precision / large numbers
|
|
305
|
+
BigFloat: { component: "ADecimalInput", fieldtype: "Decimal" },
|
|
306
|
+
BigDecimal: { component: "ADecimalInput", fieldtype: "Decimal" },
|
|
307
|
+
Decimal: { component: "ADecimalInput", fieldtype: "Decimal" },
|
|
308
|
+
BigInt: { component: "ANumericInput", fieldtype: "Int" },
|
|
309
|
+
Long: { component: "ANumericInput", fieldtype: "Int" },
|
|
310
|
+
// Identifiers
|
|
311
|
+
UUID: { component: "ATextInput", fieldtype: "Data" },
|
|
312
|
+
// Date / Time
|
|
313
|
+
DateTime: { component: "ADatetimePicker", fieldtype: "Datetime" },
|
|
314
|
+
Datetime: { component: "ADatetimePicker", fieldtype: "Datetime" },
|
|
315
|
+
Date: { component: "ADate", fieldtype: "Date" },
|
|
316
|
+
Time: { component: "ATimeInput", fieldtype: "Time" },
|
|
317
|
+
Interval: { component: "ADurationInput", fieldtype: "Duration" },
|
|
318
|
+
Duration: { component: "ADurationInput", fieldtype: "Duration" },
|
|
319
|
+
// Structured data
|
|
320
|
+
JSON: { component: "ACodeEditor", fieldtype: "JSON" },
|
|
321
|
+
JSONObject: { component: "ACodeEditor", fieldtype: "JSON" },
|
|
322
|
+
JsonNode: { component: "ACodeEditor", fieldtype: "JSON" }
|
|
323
|
+
}, re = /* @__PURE__ */ new Set(["Cursor"]);
|
|
324
|
+
function se(t) {
|
|
325
|
+
const n = { ...ae };
|
|
326
|
+
for (const [o, a] of Object.entries(oe))
|
|
327
|
+
n[o] = a;
|
|
328
|
+
if (t)
|
|
329
|
+
for (const [o, a] of Object.entries(t))
|
|
330
|
+
n[o] = {
|
|
331
|
+
component: a.component ?? "ATextInput",
|
|
332
|
+
fieldtype: a.fieldtype ?? "Data"
|
|
333
|
+
};
|
|
334
|
+
return n;
|
|
335
|
+
}
|
|
336
|
+
const ce = [
|
|
337
|
+
"Connection",
|
|
338
|
+
"Edge",
|
|
339
|
+
"Input",
|
|
340
|
+
"Patch",
|
|
341
|
+
"Payload",
|
|
342
|
+
"Condition",
|
|
343
|
+
"Filter",
|
|
344
|
+
"OrderBy",
|
|
345
|
+
"Aggregate",
|
|
346
|
+
"AggregateResult",
|
|
347
|
+
"AggregateFilter",
|
|
348
|
+
"DeleteResponse",
|
|
349
|
+
"InsertResponse",
|
|
350
|
+
"UpdateResponse",
|
|
351
|
+
"MutationResponse"
|
|
352
|
+
], le = /* @__PURE__ */ new Set(["Query", "Mutation", "Subscription"]);
|
|
353
|
+
function pe(t, n) {
|
|
354
|
+
if (t.startsWith("__") || le.has(t) || t === "Node")
|
|
355
|
+
return !1;
|
|
356
|
+
for (const a of ce)
|
|
357
|
+
if (t.endsWith(a))
|
|
358
|
+
return !1;
|
|
359
|
+
const o = n.getFields();
|
|
360
|
+
return Object.keys(o).length !== 0;
|
|
361
|
+
}
|
|
362
|
+
const de = /* @__PURE__ */ new Set(["nodeId", "__typename", "clientMutationId"]);
|
|
363
|
+
function ue(t, n, o) {
|
|
364
|
+
return !de.has(t);
|
|
365
|
+
}
|
|
366
|
+
function b(t) {
|
|
367
|
+
let n = !1, o = !1, a = t;
|
|
368
|
+
return L(a) && (n = !0, a = a.ofType), U(a) && (o = !0, a = a.ofType, L(a) && (a = a.ofType)), { namedType: a, required: n, isList: o };
|
|
369
|
+
}
|
|
370
|
+
function me(t) {
|
|
371
|
+
const o = t.getFields().edges;
|
|
372
|
+
if (!o) return;
|
|
373
|
+
const { namedType: a, isList: r } = b(o.type);
|
|
374
|
+
if (!r || !T(a)) return;
|
|
375
|
+
const d = a.getFields().node;
|
|
376
|
+
if (!d) return;
|
|
377
|
+
const { namedType: u } = b(d.type);
|
|
378
|
+
if (T(u))
|
|
379
|
+
return u.name;
|
|
380
|
+
}
|
|
381
|
+
function fe(t, n, o, a = {}) {
|
|
382
|
+
const { namedType: r, required: y, isList: d } = b(n.type), u = se(a.customScalars), i = {
|
|
383
|
+
fieldname: t,
|
|
384
|
+
label: te(t),
|
|
385
|
+
component: "ATextInput",
|
|
386
|
+
fieldtype: "Data"
|
|
387
|
+
};
|
|
388
|
+
if (y && (i.required = !0), x(r)) {
|
|
389
|
+
if (re.has(r.name))
|
|
390
|
+
return i._unmapped = !0, a.includeUnmappedMeta && (i._graphqlType = r.name), i;
|
|
391
|
+
if (r.name === "ID") {
|
|
392
|
+
const m = ne(t);
|
|
393
|
+
if (o.has(m))
|
|
394
|
+
return i.component = "ALink", i.fieldtype = "Link", i.options = h(m), i;
|
|
395
|
+
}
|
|
396
|
+
const l = u[r.name];
|
|
397
|
+
return l ? (i.component = l.component, i.fieldtype = l.fieldtype) : (i._unmapped = !0, a.includeUnmappedMeta && (i._graphqlType = r.name)), i;
|
|
398
|
+
}
|
|
399
|
+
if (j(r))
|
|
400
|
+
return i.component = "ADropdown", i.fieldtype = "Select", i.options = r.getValues().map((l) => l.name), i;
|
|
401
|
+
if (T(r)) {
|
|
402
|
+
if (!d && o.has(r.name))
|
|
403
|
+
return i.component = "ALink", i.fieldtype = "Link", i.options = h(r.name), i;
|
|
404
|
+
const l = me(r);
|
|
405
|
+
return l && o.has(l) ? (i.component = "ATable", i._isLink = !0, i.options = h(l), i.cardinality = "noneOrMany", delete i.fieldtype, i) : d && o.has(r.name) ? (i.component = "ATable", i._isLink = !0, i.options = h(r.name), i.cardinality = "noneOrMany", delete i.fieldtype, i) : (i._unmapped = !0, a.includeUnmappedMeta && (i._graphqlType = r.name), i);
|
|
406
|
+
}
|
|
407
|
+
return i._unmapped = !0, a.includeUnmappedMeta && (i._graphqlType = r.name), i;
|
|
408
|
+
}
|
|
409
|
+
function _e(t, n = {}) {
|
|
410
|
+
const o = ye(t), a = o.getTypeMap(), r = /* @__PURE__ */ new Set(), y = o.getQueryType(), d = o.getMutationType(), u = o.getSubscriptionType();
|
|
411
|
+
y && r.add(y.name), d && r.add(d.name), u && r.add(u.name);
|
|
412
|
+
const i = n.isEntityType ?? pe, l = /* @__PURE__ */ new Set();
|
|
413
|
+
for (const [c, p] of Object.entries(a))
|
|
414
|
+
T(p) && (r.has(c) || i(c, p) && l.add(c));
|
|
415
|
+
let m = l;
|
|
416
|
+
if (n.include) {
|
|
417
|
+
const c = new Set(n.include);
|
|
418
|
+
m = new Set([...l].filter((p) => c.has(p)));
|
|
419
|
+
}
|
|
420
|
+
if (n.exclude) {
|
|
421
|
+
const c = new Set(n.exclude);
|
|
422
|
+
m = new Set([...m].filter((p) => !c.has(p)));
|
|
423
|
+
}
|
|
424
|
+
const O = n.isEntityField ?? ue, w = n.deriveTableName ?? ((c) => ie(c)), I = [];
|
|
425
|
+
for (const c of m) {
|
|
426
|
+
const p = a[c];
|
|
427
|
+
if (!T(p)) continue;
|
|
428
|
+
const M = p.getFields(), F = n.typeOverrides?.[c], N = Object.entries(M).filter(([s, g]) => O(s, g, p)).map(([s, g]) => {
|
|
429
|
+
if (n.classifyField) {
|
|
430
|
+
const f = n.classifyField(s, g, p);
|
|
431
|
+
if (f != null)
|
|
432
|
+
return {
|
|
433
|
+
fieldname: s,
|
|
434
|
+
label: f.label ?? s,
|
|
435
|
+
component: f.component ?? "ATextInput",
|
|
436
|
+
fieldtype: f.fieldtype ?? "Data",
|
|
437
|
+
...f
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
const D = fe(s, g, l, n);
|
|
441
|
+
return F?.[s] ? { ...D, ...F[s] } : D;
|
|
442
|
+
}), S = {}, E = N.filter((s) => s._isLink && typeof s.options == "string" && s.cardinality ? (S[s.fieldname] = {
|
|
443
|
+
target: s.options,
|
|
444
|
+
cardinality: s.cardinality
|
|
445
|
+
}, !1) : !0).map((s) => {
|
|
446
|
+
if (!n.includeUnmappedMeta) {
|
|
447
|
+
const { _graphqlType: f, _unmapped: ge, _isLink: he, ...v } = s;
|
|
448
|
+
return v;
|
|
449
|
+
}
|
|
450
|
+
const { _isLink: g, ...D } = s;
|
|
451
|
+
return D;
|
|
452
|
+
}), A = {
|
|
453
|
+
name: c,
|
|
454
|
+
slug: h(c),
|
|
455
|
+
fields: E
|
|
456
|
+
};
|
|
457
|
+
Object.keys(S).length > 0 && (A.links = S);
|
|
458
|
+
const C = w(c);
|
|
459
|
+
C && (A.tableName = C), n.includeUnmappedMeta && (A._graphqlTypeName = c), I.push(A);
|
|
460
|
+
}
|
|
461
|
+
return I;
|
|
462
|
+
}
|
|
463
|
+
function ye(t) {
|
|
464
|
+
return typeof t == "string" ? P(t) : q(t);
|
|
465
|
+
}
|
|
466
|
+
export {
|
|
467
|
+
R as B,
|
|
468
|
+
oe as G,
|
|
469
|
+
re as I,
|
|
470
|
+
J as S,
|
|
471
|
+
W as T,
|
|
472
|
+
ae as W,
|
|
473
|
+
Ce as a,
|
|
474
|
+
se as b,
|
|
475
|
+
te as c,
|
|
476
|
+
fe as d,
|
|
477
|
+
_e as e,
|
|
478
|
+
ue as f,
|
|
479
|
+
pe as g,
|
|
480
|
+
z as h,
|
|
481
|
+
Q as i,
|
|
482
|
+
ke as j,
|
|
483
|
+
ie as k,
|
|
484
|
+
Le as l,
|
|
485
|
+
h as m,
|
|
486
|
+
Se as n,
|
|
487
|
+
Ie as p,
|
|
488
|
+
De as r,
|
|
489
|
+
Fe as s,
|
|
490
|
+
ne as t,
|
|
491
|
+
be as v
|
|
492
|
+
};
|
|
493
|
+
//# sourceMappingURL=index-BdCmYHg0.js.map
|