bunsane 0.1.4 → 0.2.0
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/.claude/settings.local.json +47 -0
- package/.claude/skills/update-memory.md +74 -0
- package/.prettierrc +4 -0
- package/.serena/memories/architectural-decision-no-dependency-injection.md +76 -0
- package/.serena/memories/architecture.md +154 -0
- package/.serena/memories/cache-interface-refactoring-2026-01-24.md +165 -0
- package/.serena/memories/code_style_and_conventions.md +76 -0
- package/.serena/memories/project_overview.md +43 -0
- package/.serena/memories/schema-dsl-plan.md +107 -0
- package/.serena/memories/suggested_commands.md +80 -0
- package/.serena/memories/typescript-compilation-status.md +54 -0
- package/.serena/project.yml +114 -0
- package/TODO.md +1 -7
- package/bun.lock +150 -4
- package/bunfig.toml +10 -0
- package/config/cache.config.ts +77 -0
- package/config/upload.config.ts +4 -5
- package/core/App.ts +870 -123
- package/core/ArcheType.ts +2268 -377
- package/core/BatchLoader.ts +181 -71
- package/core/Config.ts +153 -0
- package/core/Decorators.ts +4 -1
- package/core/Entity.ts +621 -92
- package/core/EntityHookManager.ts +1 -1
- package/core/EntityInterface.ts +3 -1
- package/core/EntityManager.ts +1 -13
- package/core/ErrorHandler.ts +8 -2
- package/core/Logger.ts +9 -0
- package/core/Middleware.ts +34 -0
- package/core/RequestContext.ts +5 -1
- package/core/RequestLoaders.ts +227 -93
- package/core/SchedulerManager.ts +193 -52
- package/core/cache/CacheAnalytics.ts +399 -0
- package/core/cache/CacheFactory.ts +145 -0
- package/core/cache/CacheManager.ts +520 -0
- package/core/cache/CacheProvider.ts +34 -0
- package/core/cache/CacheWarmer.ts +157 -0
- package/core/cache/CompressionUtils.ts +110 -0
- package/core/cache/MemoryCache.ts +251 -0
- package/core/cache/MultiLevelCache.ts +180 -0
- package/core/cache/NoOpCache.ts +53 -0
- package/core/cache/RedisCache.ts +464 -0
- package/core/cache/TTLStrategy.ts +254 -0
- package/core/cache/index.ts +6 -0
- package/core/components/BaseComponent.ts +120 -0
- package/core/{ComponentRegistry.ts → components/ComponentRegistry.ts} +148 -54
- package/core/components/Decorators.ts +88 -0
- package/core/components/Interfaces.ts +7 -0
- package/core/components/index.ts +5 -0
- package/core/decorators/EntityHooks.ts +0 -3
- package/core/decorators/IndexedField.ts +26 -0
- package/core/decorators/ScheduledTask.ts +0 -47
- package/core/events/EntityLifecycleEvents.ts +1 -1
- package/core/health.ts +112 -0
- package/core/metadata/definitions/ArcheType.ts +14 -0
- package/core/metadata/definitions/Component.ts +9 -0
- package/core/metadata/definitions/gqlObject.ts +1 -1
- package/core/metadata/index.ts +42 -1
- package/core/metadata/metadata-storage.ts +28 -2
- package/core/middleware/AccessLog.ts +59 -0
- package/core/middleware/RequestId.ts +38 -0
- package/core/middleware/SecurityHeaders.ts +62 -0
- package/core/middleware/index.ts +3 -0
- package/core/scheduler/DistributedLock.ts +266 -0
- package/core/scheduler/index.ts +15 -0
- package/core/validateEnv.ts +92 -0
- package/database/DatabaseHelper.ts +416 -40
- package/database/IndexingStrategy.ts +342 -0
- package/database/PreparedStatementCache.ts +226 -0
- package/database/index.ts +32 -7
- package/database/sqlHelpers.ts +14 -2
- package/endpoints/archetypes.ts +362 -0
- package/endpoints/components.ts +58 -0
- package/endpoints/entity.ts +80 -0
- package/endpoints/index.ts +27 -0
- package/endpoints/query.ts +93 -0
- package/endpoints/stats.ts +76 -0
- package/endpoints/tables.ts +212 -0
- package/endpoints/types.ts +155 -0
- package/gql/ArchetypeOperations.ts +32 -86
- package/gql/Generator.ts +27 -315
- package/gql/GeneratorV2.ts +37 -0
- package/gql/builders/InputTypeBuilder.ts +99 -0
- package/gql/builders/ResolverBuilder.ts +234 -0
- package/gql/builders/TypeDefBuilder.ts +105 -0
- package/gql/builders/index.ts +3 -0
- package/gql/decorators/Upload.ts +1 -1
- package/gql/depthLimit.ts +85 -0
- package/gql/graph/GraphNode.ts +224 -0
- package/gql/graph/SchemaGraph.ts +278 -0
- package/gql/helpers.ts +8 -2
- package/gql/index.ts +56 -4
- package/gql/middleware.ts +79 -0
- package/gql/orchestration/GraphQLSchemaOrchestrator.ts +241 -0
- package/gql/orchestration/index.ts +1 -0
- package/gql/scanner/ServiceScanner.ts +347 -0
- package/gql/schema/index.ts +458 -0
- package/gql/strategies/TypeGenerationStrategy.ts +329 -0
- package/gql/types.ts +1 -0
- package/gql/utils/TypeSignature.ts +220 -0
- package/gql/utils/index.ts +1 -0
- package/gql/visitors/ArchetypePreprocessorVisitor.ts +80 -0
- package/gql/visitors/DeduplicationVisitor.ts +82 -0
- package/gql/visitors/GraphVisitor.ts +78 -0
- package/gql/visitors/ResolverGeneratorVisitor.ts +122 -0
- package/gql/visitors/SchemaGeneratorVisitor.ts +851 -0
- package/gql/visitors/TypeCollectorVisitor.ts +79 -0
- package/gql/visitors/VisitorComposer.ts +96 -0
- package/gql/visitors/index.ts +7 -0
- package/package.json +59 -37
- package/plugins/index.ts +2 -2
- package/query/CTENode.ts +97 -0
- package/query/ComponentInclusionNode.ts +689 -0
- package/query/FilterBuilder.ts +127 -0
- package/query/FilterBuilderRegistry.ts +202 -0
- package/query/OrNode.ts +517 -0
- package/query/OrQuery.ts +42 -0
- package/query/Query.ts +1022 -0
- package/query/QueryContext.ts +170 -0
- package/query/QueryDAG.ts +122 -0
- package/query/QueryNode.ts +65 -0
- package/query/SourceNode.ts +53 -0
- package/query/builders/FullTextSearchBuilder.ts +236 -0
- package/query/index.ts +21 -0
- package/scheduler/index.ts +40 -8
- package/service/Service.ts +2 -1
- package/service/ServiceRegistry.ts +6 -5
- package/{core/storage → storage}/LocalStorageProvider.ts +2 -2
- package/storage/S3StorageProvider.ts +316 -0
- package/{core/storage → storage}/StorageProvider.ts +7 -3
- package/studio/bun.lock +482 -0
- package/studio/index.html +13 -0
- package/studio/package.json +39 -0
- package/studio/postcss.config.js +6 -0
- package/studio/src/components/DataTable.tsx +211 -0
- package/studio/src/components/Layout.tsx +13 -0
- package/studio/src/components/PageContainer.tsx +9 -0
- package/studio/src/components/PageHeader.tsx +13 -0
- package/studio/src/components/SearchBar.tsx +57 -0
- package/studio/src/components/Sidebar.tsx +294 -0
- package/studio/src/components/ui/button.tsx +56 -0
- package/studio/src/components/ui/checkbox.tsx +26 -0
- package/studio/src/components/ui/input.tsx +25 -0
- package/studio/src/hooks/useDataTable.ts +131 -0
- package/studio/src/index.css +36 -0
- package/studio/src/lib/api.ts +186 -0
- package/studio/src/lib/utils.ts +13 -0
- package/studio/src/main.tsx +17 -0
- package/studio/src/pages/ArcheType.tsx +239 -0
- package/studio/src/pages/Components.tsx +124 -0
- package/studio/src/pages/EntityInspector.tsx +302 -0
- package/studio/src/pages/QueryRunner.tsx +246 -0
- package/studio/src/pages/Table.tsx +94 -0
- package/studio/src/pages/Welcome.tsx +241 -0
- package/studio/src/routes.tsx +45 -0
- package/studio/src/store/archeTypeSettings.ts +30 -0
- package/studio/src/store/studio.ts +65 -0
- package/studio/src/utils/columnHelpers.tsx +114 -0
- package/studio/studio-instructions.md +81 -0
- package/studio/tailwind.config.js +77 -0
- package/studio/tsconfig.json +24 -0
- package/studio/utils.ts +54 -0
- package/studio/vite.config.js +19 -0
- package/swagger/generator.ts +1 -1
- package/tests/e2e/http.test.ts +126 -0
- package/tests/fixtures/archetypes/TestUserArchetype.ts +21 -0
- package/tests/fixtures/components/TestOrder.ts +23 -0
- package/tests/fixtures/components/TestProduct.ts +23 -0
- package/tests/fixtures/components/TestUser.ts +20 -0
- package/tests/fixtures/components/index.ts +6 -0
- package/tests/graphql/SchemaGeneration.test.ts +90 -0
- package/tests/graphql/builders/ResolverBuilder.test.ts +223 -0
- package/tests/graphql/builders/TypeDefBuilder.test.ts +153 -0
- package/tests/integration/archetype/ArcheType.persistence.test.ts +241 -0
- package/tests/integration/cache/CacheInvalidation.test.ts +259 -0
- package/tests/integration/entity/Entity.persistence.test.ts +333 -0
- package/tests/integration/query/Query.exec.test.ts +523 -0
- package/tests/pglite-setup.ts +61 -0
- package/tests/setup.ts +164 -0
- package/tests/stress/BenchmarkRunner.ts +203 -0
- package/tests/stress/DataSeeder.ts +190 -0
- package/tests/stress/StressTestReporter.ts +229 -0
- package/tests/stress/cursor-perf-test.ts +171 -0
- package/tests/stress/fixtures/StressTestComponents.ts +58 -0
- package/tests/stress/index.ts +7 -0
- package/tests/stress/scenarios/query-benchmarks.test.ts +285 -0
- package/tests/unit/BatchLoader.test.ts +82 -0
- package/tests/unit/archetype/ArcheType.test.ts +107 -0
- package/tests/unit/cache/CacheManager.test.ts +347 -0
- package/tests/unit/cache/MemoryCache.test.ts +260 -0
- package/tests/unit/cache/RedisCache.test.ts +411 -0
- package/tests/unit/entity/Entity.components.test.ts +244 -0
- package/tests/unit/entity/Entity.test.ts +345 -0
- package/tests/unit/gql/depthLimit.test.ts +203 -0
- package/tests/unit/gql/operationMiddleware.test.ts +293 -0
- package/tests/unit/health/Health.test.ts +129 -0
- package/tests/unit/middleware/AccessLog.test.ts +37 -0
- package/tests/unit/middleware/Middleware.test.ts +98 -0
- package/tests/unit/middleware/RequestId.test.ts +54 -0
- package/tests/unit/middleware/SecurityHeaders.test.ts +66 -0
- package/tests/unit/query/FilterBuilder.test.ts +111 -0
- package/tests/unit/query/Query.test.ts +308 -0
- package/tests/unit/scheduler/DistributedLock.test.ts +274 -0
- package/tests/unit/schema/schema-integration.test.ts +426 -0
- package/tests/unit/schema/schema.test.ts +580 -0
- package/tests/unit/storage/S3StorageProvider.test.ts +571 -0
- package/tests/unit/upload/RestUpload.test.ts +267 -0
- package/tests/unit/validateEnv.test.ts +82 -0
- package/tests/utils/entity-tracker.ts +57 -0
- package/tests/utils/index.ts +13 -0
- package/tests/utils/test-context.ts +149 -0
- package/tsconfig.json +5 -1
- package/types/archetype.types.ts +6 -0
- package/types/hooks.types.ts +1 -1
- package/types/query.types.ts +110 -0
- package/types/scheduler.types.ts +68 -7
- package/types/upload.types.ts +1 -0
- package/{core → upload}/FileValidator.ts +10 -1
- package/upload/RestUpload.ts +130 -0
- package/{core/components → upload}/UploadComponent.ts +11 -11
- package/{core → upload}/UploadManager.ts +3 -3
- package/upload/index.ts +23 -7
- package/utils/UploadHelper.ts +27 -6
- package/utils/cronParser.ts +16 -6
- package/.github/workflows/deploy-docs.yml +0 -57
- package/core/Components.ts +0 -202
- package/core/EntityCache.ts +0 -15
- package/core/Query.ts +0 -880
- package/docs/README.md +0 -149
- package/docs/_coverpage.md +0 -36
- package/docs/_sidebar.md +0 -23
- package/docs/api/core.md +0 -568
- package/docs/api/hooks.md +0 -554
- package/docs/api/index.md +0 -222
- package/docs/api/query.md +0 -678
- package/docs/api/service.md +0 -744
- package/docs/core-concepts/archetypes.md +0 -512
- package/docs/core-concepts/components.md +0 -498
- package/docs/core-concepts/entity.md +0 -314
- package/docs/core-concepts/hooks.md +0 -683
- package/docs/core-concepts/query.md +0 -588
- package/docs/core-concepts/services.md +0 -647
- package/docs/examples/code-examples.md +0 -425
- package/docs/getting-started.md +0 -337
- package/docs/index.html +0 -97
- package/tests/bench/insert.bench.ts +0 -60
- package/tests/bench/relations.bench.ts +0 -270
- package/tests/bench/sorting.bench.ts +0 -416
- package/tests/component-hooks-simple.test.ts +0 -117
- package/tests/component-hooks.test.ts +0 -1461
- package/tests/component.test.ts +0 -339
- package/tests/errorHandling.test.ts +0 -155
- package/tests/hooks.test.ts +0 -667
- package/tests/query-sorting.test.ts +0 -101
- package/tests/query.test.ts +0 -81
- package/tests/relations.test.ts +0 -170
- package/tests/scheduler.test.ts +0 -724
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import type { GraphQLType } from "../helpers";
|
|
2
|
+
import type { ZodType } from "zod";
|
|
3
|
+
import BaseArcheType from "../../core/ArcheType";
|
|
4
|
+
import { logger } from "../../core/Logger";
|
|
5
|
+
|
|
6
|
+
export interface TypeGenerationResult {
|
|
7
|
+
typeDef: string;
|
|
8
|
+
fieldType?: string;
|
|
9
|
+
resolverWrapper?: (resolver: Function) => Function;
|
|
10
|
+
inputTypeName?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TypeGenerationContext {
|
|
14
|
+
isInput: boolean;
|
|
15
|
+
operationName: string;
|
|
16
|
+
definedInputTypes: Set<string>;
|
|
17
|
+
scalarTypes: Set<string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TypeGenerationStrategy {
|
|
21
|
+
canHandle(meta: any): boolean;
|
|
22
|
+
generateTypeDef(meta: any, context: TypeGenerationContext): TypeGenerationResult;
|
|
23
|
+
generateResolver?(originalResolver: Function, meta: any): Function;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Strategy for Zod schema-based input generation
|
|
28
|
+
*/
|
|
29
|
+
export class ZodTypeStrategy implements TypeGenerationStrategy {
|
|
30
|
+
canHandle(meta: any): boolean {
|
|
31
|
+
return !!(meta && typeof meta === 'object' && '_def' in meta);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
generateTypeDef(meta: ZodType, context: TypeGenerationContext): TypeGenerationResult {
|
|
35
|
+
// Extracted logic from existing generator for Zod inputs
|
|
36
|
+
const inputName = `${context.operationName}Input`;
|
|
37
|
+
let originalInput = meta;
|
|
38
|
+
|
|
39
|
+
// Handle optional schemas
|
|
40
|
+
let innerInput: any = meta;
|
|
41
|
+
const ZodOptional = (require('zod')).ZodOptional;
|
|
42
|
+
const wasOptional = meta instanceof ZodOptional;
|
|
43
|
+
if (wasOptional && 'unwrap' in (meta as any)) {
|
|
44
|
+
innerInput = (meta as any).unwrap();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Preprocess schema for unions with scalar literals
|
|
48
|
+
const innerDef = (innerInput as any)._def;
|
|
49
|
+
const shape = innerDef && (typeof innerDef.shape === 'function' ? innerDef.shape() : innerDef.shape);
|
|
50
|
+
if (shape) {
|
|
51
|
+
const processedShape: any = {};
|
|
52
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
53
|
+
const fieldSchema = value as any;
|
|
54
|
+
const typeName = fieldSchema._def?.typeName || fieldSchema._def?.type;
|
|
55
|
+
|
|
56
|
+
if (typeName === 'ZodUnion' || typeName === 'union') {
|
|
57
|
+
const options = fieldSchema._def?.options || [];
|
|
58
|
+
let foundScalarLiteral = null;
|
|
59
|
+
|
|
60
|
+
for (const option of options) {
|
|
61
|
+
const optionTypeName = option._def?.typeName || option._def?.type;
|
|
62
|
+
if (optionTypeName === 'ZodLiteral' || optionTypeName === 'literal') {
|
|
63
|
+
const value = option._def?.value ?? (option._def?.values ? option._def.values[0] : undefined);
|
|
64
|
+
if (typeof value === 'string' && context.scalarTypes.has(value)) {
|
|
65
|
+
foundScalarLiteral = option;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
processedShape[key] = foundScalarLiteral || fieldSchema;
|
|
72
|
+
} else {
|
|
73
|
+
processedShape[key] = fieldSchema;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
innerInput = (require('zod')).object(processedShape);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Add __typename field if extend is available
|
|
81
|
+
const z = require('zod');
|
|
82
|
+
if (innerInput && typeof (innerInput as any).extend === 'function') {
|
|
83
|
+
innerInput = (innerInput as any).extend({ __typename: z.literal(inputName).nullish() });
|
|
84
|
+
}
|
|
85
|
+
if (wasOptional) {
|
|
86
|
+
meta = innerInput.optional();
|
|
87
|
+
} else {
|
|
88
|
+
meta = innerInput;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const { weave, ZodWeaver } = require('@gqloom/core');
|
|
92
|
+
const { printSchema } = require('graphql');
|
|
93
|
+
const gqlInputSchema = weave(ZodWeaver, meta as ZodType) as any;
|
|
94
|
+
const schemaString = printSchema(gqlInputSchema);
|
|
95
|
+
|
|
96
|
+
const typeNames: string[] = [];
|
|
97
|
+
schemaString.replace(/type (\w+)/g, (_match: string, name: string) => {
|
|
98
|
+
typeNames.push(name);
|
|
99
|
+
return _match;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
let inputTypeDefs = schemaString.replace(/\btype\b/g, 'input');
|
|
103
|
+
inputTypeDefs = inputTypeDefs.replace(/input (\w+)/g, (_match: string, name: string) => {
|
|
104
|
+
if (name.endsWith('Input')) {
|
|
105
|
+
return `input ${name}`;
|
|
106
|
+
} else {
|
|
107
|
+
return `input ${name}Input`;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
inputTypeDefs = inputTypeDefs.replace(/: (\[?)(\w+)([!\[\]]*)(\s|$)/g, (_match: string, bracketStart: string, type: string, suffix: string, end: string) => {
|
|
112
|
+
if (typeNames.includes(type)) {
|
|
113
|
+
return `: ${bracketStart}${type.endsWith('Input') ? type : type + 'Input'}${suffix}${end}`;
|
|
114
|
+
} else {
|
|
115
|
+
return _match;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Deduplicate
|
|
120
|
+
const deduplicatedInputTypeDefs = this.deduplicateInputTypes(inputTypeDefs, context.definedInputTypes);
|
|
121
|
+
let typeDef = deduplicatedInputTypeDefs + '\n';
|
|
122
|
+
|
|
123
|
+
// Post-process for literal scalars
|
|
124
|
+
let schemaToTraverse: any = originalInput;
|
|
125
|
+
const defType = (schemaToTraverse._def as any)?.typeName || (schemaToTraverse._def as any)?.type;
|
|
126
|
+
if (defType === 'ZodOptional' || defType === 'optional') {
|
|
127
|
+
schemaToTraverse = (schemaToTraverse as any)._def.innerType;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const literalFields: Record<string, string> = {};
|
|
131
|
+
this.traverseZod(schemaToTraverse, literalFields, context.scalarTypes);
|
|
132
|
+
|
|
133
|
+
for (const [fieldPath, scalarName] of Object.entries(literalFields)) {
|
|
134
|
+
const fieldName = fieldPath.split('.').pop()!;
|
|
135
|
+
typeDef = typeDef.replace(new RegExp(`(\\s+${fieldName}:\\s+)String!`, 'g'), `$1${scalarName}!`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Determine nullability based on whether the input was optional
|
|
139
|
+
const inputNullability = wasOptional ? '' : '!';
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
typeDef,
|
|
143
|
+
fieldType: `(input: ${inputName}${inputNullability})`,
|
|
144
|
+
inputTypeName: inputName,
|
|
145
|
+
resolverWrapper: (resolver) => this.wrapResolverWithZodValidation(resolver, originalInput)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private deduplicateInputTypes(inputTypeDefs: string, definedTypes: Set<string>): string {
|
|
150
|
+
const lines = inputTypeDefs.split('\n');
|
|
151
|
+
const result: string[] = [];
|
|
152
|
+
let currentType = '';
|
|
153
|
+
let currentTypeName = '';
|
|
154
|
+
let inTypeDefinition = false;
|
|
155
|
+
|
|
156
|
+
for (const line of lines) {
|
|
157
|
+
if (line.startsWith('input ')) {
|
|
158
|
+
currentTypeName = line.split(' ')[1] ?? '';
|
|
159
|
+
if (definedTypes.has(currentTypeName)) {
|
|
160
|
+
inTypeDefinition = false;
|
|
161
|
+
currentType = '';
|
|
162
|
+
continue;
|
|
163
|
+
} else {
|
|
164
|
+
definedTypes.add(currentTypeName);
|
|
165
|
+
inTypeDefinition = true;
|
|
166
|
+
currentType = line + '\n';
|
|
167
|
+
}
|
|
168
|
+
} else if (inTypeDefinition) {
|
|
169
|
+
currentType += line + '\n';
|
|
170
|
+
if (line.trim() === '}') {
|
|
171
|
+
result.push(currentType);
|
|
172
|
+
inTypeDefinition = false;
|
|
173
|
+
currentType = '';
|
|
174
|
+
}
|
|
175
|
+
} else if (!inTypeDefinition && line.trim()) {
|
|
176
|
+
result.push(line + '\n');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return result.join('');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private traverseZod(obj: any, literalFields: Record<string, string>, scalarTypes: Set<string>, path: string[] = []) {
|
|
184
|
+
if (!obj || !obj._def) return;
|
|
185
|
+
const typeName = (obj._def as any).typeName || (obj._def as any).type;
|
|
186
|
+
if (typeName === 'ZodLiteral' || typeName === 'literal') {
|
|
187
|
+
const defObj = obj._def as any;
|
|
188
|
+
const value = defObj.value ?? (defObj.values ? defObj.values[0] : undefined);
|
|
189
|
+
if (typeof value === 'string' && scalarTypes.has(value)) {
|
|
190
|
+
literalFields[path.join('.')] = value;
|
|
191
|
+
}
|
|
192
|
+
} else if (typeName === 'ZodUnion' || typeName === 'union') {
|
|
193
|
+
const options = (obj._def as any).options || [];
|
|
194
|
+
for (const option of options) {
|
|
195
|
+
const optionTypeName = (option._def as any)?.typeName || (option._def as any)?.type;
|
|
196
|
+
if (optionTypeName === 'ZodLiteral' || optionTypeName === 'literal') {
|
|
197
|
+
const defObj = option._def as any;
|
|
198
|
+
const value = defObj.value ?? (defObj.values ? defObj.values[0] : undefined);
|
|
199
|
+
if (typeof value === 'string' && scalarTypes.has(value)) {
|
|
200
|
+
literalFields[path.join('.')] = value;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
} else if (typeName === 'ZodObject' || typeName === 'object') {
|
|
206
|
+
const shape = typeof obj._def.shape === 'function' ? obj._def.shape() : obj._def.shape;
|
|
207
|
+
if (shape) {
|
|
208
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
209
|
+
this.traverseZod(value, literalFields, scalarTypes, [...path, key]);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private wrapResolverWithZodValidation(resolver: Function, zodSchema: ZodType): Function {
|
|
216
|
+
return async (_: any, args: any, context: any, info: any) => {
|
|
217
|
+
try {
|
|
218
|
+
const inputArgs = args.input || args;
|
|
219
|
+
const validated = zodSchema.parse(inputArgs);
|
|
220
|
+
return await resolver(validated, context, info);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (error instanceof (require('zod')).ZodError) {
|
|
223
|
+
const { handleGraphQLError } = await import("../../core/ErrorHandler");
|
|
224
|
+
handleGraphQLError(error);
|
|
225
|
+
}
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Strategy for archetype output types
|
|
234
|
+
*/
|
|
235
|
+
export class ArchetypeTypeStrategy implements TypeGenerationStrategy {
|
|
236
|
+
canHandle(meta: any): boolean {
|
|
237
|
+
return meta instanceof BaseArcheType || (Array.isArray(meta) && meta[0] instanceof BaseArcheType);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
generateTypeDef(meta: BaseArcheType | BaseArcheType[], context: TypeGenerationContext): TypeGenerationResult {
|
|
241
|
+
const { getArchetypeTypeName } = require("../../core/ArcheType");
|
|
242
|
+
|
|
243
|
+
if (Array.isArray(meta)) {
|
|
244
|
+
const archetypeInstance = meta[0];
|
|
245
|
+
const typeName = getArchetypeTypeName(archetypeInstance);
|
|
246
|
+
if (typeName) {
|
|
247
|
+
return { typeDef: '', fieldType: `: [${typeName}]` };
|
|
248
|
+
} else {
|
|
249
|
+
logger.warn(`Invalid array output type for ${context.operationName}, expected archetype instance`);
|
|
250
|
+
return { typeDef: '', fieldType: `: [Any]` };
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
const typeName = getArchetypeTypeName(meta);
|
|
254
|
+
if (typeName) {
|
|
255
|
+
return { typeDef: '', fieldType: `: ${typeName}` };
|
|
256
|
+
} else {
|
|
257
|
+
logger.warn(`Could not determine type name for archetype in ${context.operationName}`);
|
|
258
|
+
return { typeDef: '', fieldType: `: Any` };
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Strategy for legacy Record<string, GraphQLType> format
|
|
266
|
+
*/
|
|
267
|
+
export class LegacyTypeStrategy implements TypeGenerationStrategy {
|
|
268
|
+
canHandle(meta: any): boolean {
|
|
269
|
+
return meta && typeof meta === 'object' && !('_def' in meta) && !this.isArchetype(meta);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private isArchetype(meta: any): boolean {
|
|
273
|
+
return meta instanceof BaseArcheType || (Array.isArray(meta) && meta[0] instanceof BaseArcheType);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
generateTypeDef(meta: Record<string, GraphQLType>, context: TypeGenerationContext): TypeGenerationResult {
|
|
277
|
+
if (context.isInput) {
|
|
278
|
+
// Input type
|
|
279
|
+
const inputName = `${context.operationName}Input`;
|
|
280
|
+
const typeDef = `input ${inputName} {\n${Object.entries(meta).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
|
|
281
|
+
return { typeDef, fieldType: `(input: ${inputName}!)`, inputTypeName: inputName };
|
|
282
|
+
} else {
|
|
283
|
+
// Output type
|
|
284
|
+
const outputName = `${context.operationName}Output`;
|
|
285
|
+
const typeDef = `type ${outputName} {\n${Object.entries(meta).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
|
|
286
|
+
return { typeDef, fieldType: `: ${outputName}` };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private isInputLike(meta: Record<string, GraphQLType>): boolean {
|
|
291
|
+
// Heuristic: if operationName ends with 'Input' or has input-like fields, treat as input
|
|
292
|
+
// For now, assume if it's used as input in context, but since we don't have context, check field names
|
|
293
|
+
return true; // Default to input for legacy, adjust based on usage
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Strategy for primitive string-based types
|
|
299
|
+
*/
|
|
300
|
+
export class PrimitiveTypeStrategy implements TypeGenerationStrategy {
|
|
301
|
+
canHandle(meta: any): boolean {
|
|
302
|
+
return typeof meta === 'string';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
generateTypeDef(meta: string, context: TypeGenerationContext): TypeGenerationResult {
|
|
306
|
+
return { typeDef: '', fieldType: `: ${meta}` };
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Factory to select the appropriate strategy
|
|
312
|
+
*/
|
|
313
|
+
export class TypeGenerationStrategyFactory {
|
|
314
|
+
private static strategies: TypeGenerationStrategy[] = [
|
|
315
|
+
new ZodTypeStrategy(),
|
|
316
|
+
new ArchetypeTypeStrategy(),
|
|
317
|
+
new LegacyTypeStrategy(),
|
|
318
|
+
new PrimitiveTypeStrategy()
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
static getStrategy(meta: any): TypeGenerationStrategy {
|
|
322
|
+
for (const strategy of this.strategies) {
|
|
323
|
+
if (strategy.canHandle(meta)) {
|
|
324
|
+
return strategy;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
throw new Error(`No strategy found for meta: ${JSON.stringify(meta)}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
package/gql/types.ts
CHANGED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { ZodType } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate a structural signature for a Zod schema to enable type matching.
|
|
5
|
+
* The signature is based on field names and their types, sorted alphabetically.
|
|
6
|
+
* This allows us to detect when two schemas are structurally equivalent,
|
|
7
|
+
* even if they were created through different transformations (.omit(), .extend(), etc.)
|
|
8
|
+
*/
|
|
9
|
+
export function generateZodStructuralSignature(schema: ZodType | any): string {
|
|
10
|
+
if (!schema || !schema._def) return 'unknown';
|
|
11
|
+
|
|
12
|
+
const typeName = schema._def.typeName || schema._def.type;
|
|
13
|
+
|
|
14
|
+
// Handle object types - the main case for input type matching
|
|
15
|
+
if (typeName === 'ZodObject' || typeName === 'object') {
|
|
16
|
+
let shape: Record<string, any> | undefined;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
shape = typeof schema._def.shape === 'function'
|
|
20
|
+
? schema._def.shape()
|
|
21
|
+
: schema._def.shape;
|
|
22
|
+
} catch {
|
|
23
|
+
// If shape access fails, try alternative methods
|
|
24
|
+
if (schema.shape && typeof schema.shape === 'object') {
|
|
25
|
+
shape = schema.shape;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!shape || Object.keys(shape).length === 0) {
|
|
30
|
+
return 'object:{}';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Sort keys for consistent hashing
|
|
34
|
+
const fieldSignatures = Object.entries(shape)
|
|
35
|
+
.filter(([key]) => key !== '__typename') // Exclude __typename from signature
|
|
36
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
37
|
+
.map(([key, value]) => `${key}:${generateZodStructuralSignature(value as ZodType)}`)
|
|
38
|
+
.join(',');
|
|
39
|
+
|
|
40
|
+
return `object:{${fieldSignatures}}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Primitive types
|
|
44
|
+
if (typeName === 'ZodNumber' || typeName === 'number') return 'number';
|
|
45
|
+
if (typeName === 'ZodString' || typeName === 'string') return 'string';
|
|
46
|
+
if (typeName === 'ZodBoolean' || typeName === 'boolean') return 'boolean';
|
|
47
|
+
if (typeName === 'ZodDate' || typeName === 'date') return 'date';
|
|
48
|
+
if (typeName === 'ZodAny' || typeName === 'any') return 'any';
|
|
49
|
+
if (typeName === 'ZodUnknown' || typeName === 'unknown') return 'unknown';
|
|
50
|
+
if (typeName === 'ZodVoid' || typeName === 'void') return 'void';
|
|
51
|
+
if (typeName === 'ZodNull' || typeName === 'null') return 'null';
|
|
52
|
+
if (typeName === 'ZodUndefined' || typeName === 'undefined') return 'undefined';
|
|
53
|
+
if (typeName === 'ZodNaN' || typeName === 'nan') return 'nan';
|
|
54
|
+
if (typeName === 'ZodBigInt' || typeName === 'bigint') return 'bigint';
|
|
55
|
+
if (typeName === 'ZodSymbol' || typeName === 'symbol') return 'symbol';
|
|
56
|
+
|
|
57
|
+
// Wrapper types - unwrap and include wrapper info
|
|
58
|
+
if (typeName === 'ZodOptional' || typeName === 'optional') {
|
|
59
|
+
const inner = generateZodStructuralSignature(schema._def.innerType);
|
|
60
|
+
return `optional:${inner}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (typeName === 'ZodNullable' || typeName === 'nullable') {
|
|
64
|
+
const inner = generateZodStructuralSignature(schema._def.innerType);
|
|
65
|
+
return `nullable:${inner}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (typeName === 'ZodDefault' || typeName === 'default') {
|
|
69
|
+
const inner = generateZodStructuralSignature(schema._def.innerType);
|
|
70
|
+
return `default:${inner}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeName === 'ZodEffects' || typeName === 'effects') {
|
|
74
|
+
const inner = generateZodStructuralSignature(schema._def.schema);
|
|
75
|
+
return `effects:${inner}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (typeName === 'ZodReadonly' || typeName === 'readonly') {
|
|
79
|
+
const inner = generateZodStructuralSignature(schema._def.innerType);
|
|
80
|
+
return `readonly:${inner}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (typeName === 'ZodBranded' || typeName === 'branded') {
|
|
84
|
+
const inner = generateZodStructuralSignature(schema._def.type);
|
|
85
|
+
return `branded:${inner}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (typeName === 'ZodCatch' || typeName === 'catch') {
|
|
89
|
+
const inner = generateZodStructuralSignature(schema._def.innerType);
|
|
90
|
+
return `catch:${inner}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (typeName === 'ZodPipeline' || typeName === 'pipeline') {
|
|
94
|
+
const inner = generateZodStructuralSignature(schema._def.in);
|
|
95
|
+
const outer = generateZodStructuralSignature(schema._def.out);
|
|
96
|
+
return `pipeline:${inner}->${outer}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Array type
|
|
100
|
+
if (typeName === 'ZodArray' || typeName === 'array') {
|
|
101
|
+
const elementType = schema._def.type;
|
|
102
|
+
const elementSignature = generateZodStructuralSignature(elementType);
|
|
103
|
+
return `array:${elementSignature}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Tuple type
|
|
107
|
+
if (typeName === 'ZodTuple' || typeName === 'tuple') {
|
|
108
|
+
const items = schema._def.items || [];
|
|
109
|
+
const itemSignatures = items.map((item: ZodType) => generateZodStructuralSignature(item)).join(',');
|
|
110
|
+
const rest = schema._def.rest ? `,rest:${generateZodStructuralSignature(schema._def.rest)}` : '';
|
|
111
|
+
return `tuple:[${itemSignatures}${rest}]`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Union type - sort options for consistent signature
|
|
115
|
+
if (typeName === 'ZodUnion' || typeName === 'union') {
|
|
116
|
+
const options = schema._def.options || [];
|
|
117
|
+
const optionSignatures = options
|
|
118
|
+
.map((opt: ZodType) => generateZodStructuralSignature(opt))
|
|
119
|
+
.sort()
|
|
120
|
+
.join('|');
|
|
121
|
+
return `union:(${optionSignatures})`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Discriminated union - include discriminator key
|
|
125
|
+
if (typeName === 'ZodDiscriminatedUnion' || typeName === 'discriminatedUnion') {
|
|
126
|
+
const discriminator = schema._def.discriminator;
|
|
127
|
+
const options = schema._def.options || [];
|
|
128
|
+
const optionSignatures = options
|
|
129
|
+
.map((opt: ZodType) => generateZodStructuralSignature(opt))
|
|
130
|
+
.sort()
|
|
131
|
+
.join('|');
|
|
132
|
+
return `discUnion:${discriminator}:(${optionSignatures})`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Intersection type
|
|
136
|
+
if (typeName === 'ZodIntersection' || typeName === 'intersection') {
|
|
137
|
+
const left = generateZodStructuralSignature(schema._def.left);
|
|
138
|
+
const right = generateZodStructuralSignature(schema._def.right);
|
|
139
|
+
return `intersection:(${left}&${right})`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Literal type - include the literal value
|
|
143
|
+
if (typeName === 'ZodLiteral' || typeName === 'literal') {
|
|
144
|
+
const value = schema._def.value ?? (schema._def.values ? schema._def.values[0] : undefined);
|
|
145
|
+
return `literal:${JSON.stringify(value)}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Enum type
|
|
149
|
+
if (typeName === 'ZodEnum' || typeName === 'enum') {
|
|
150
|
+
const values = schema._def.values || [];
|
|
151
|
+
return `enum:[${values.sort().join(',')}]`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Native enum type
|
|
155
|
+
if (typeName === 'ZodNativeEnum' || typeName === 'nativeEnum') {
|
|
156
|
+
const values = Object.values(schema._def.values || {}).sort();
|
|
157
|
+
return `nativeEnum:[${values.join(',')}]`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Record type
|
|
161
|
+
if (typeName === 'ZodRecord' || typeName === 'record') {
|
|
162
|
+
const keyType = generateZodStructuralSignature(schema._def.keyType);
|
|
163
|
+
const valueType = generateZodStructuralSignature(schema._def.valueType);
|
|
164
|
+
return `record:<${keyType},${valueType}>`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Map type
|
|
168
|
+
if (typeName === 'ZodMap' || typeName === 'map') {
|
|
169
|
+
const keyType = generateZodStructuralSignature(schema._def.keyType);
|
|
170
|
+
const valueType = generateZodStructuralSignature(schema._def.valueType);
|
|
171
|
+
return `map:<${keyType},${valueType}>`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Set type
|
|
175
|
+
if (typeName === 'ZodSet' || typeName === 'set') {
|
|
176
|
+
const valueType = generateZodStructuralSignature(schema._def.valueType);
|
|
177
|
+
return `set:<${valueType}>`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Promise type
|
|
181
|
+
if (typeName === 'ZodPromise' || typeName === 'promise') {
|
|
182
|
+
const inner = generateZodStructuralSignature(schema._def.type);
|
|
183
|
+
return `promise:${inner}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Function type
|
|
187
|
+
if (typeName === 'ZodFunction' || typeName === 'function') {
|
|
188
|
+
const args = generateZodStructuralSignature(schema._def.args);
|
|
189
|
+
const returns = generateZodStructuralSignature(schema._def.returns);
|
|
190
|
+
return `function:(${args})=>${returns}`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Lazy type - be careful with infinite recursion
|
|
194
|
+
if (typeName === 'ZodLazy' || typeName === 'lazy') {
|
|
195
|
+
// For lazy types, we can't fully resolve without risking infinite recursion
|
|
196
|
+
// Use a placeholder that indicates it's a lazy type
|
|
197
|
+
return 'lazy:deferred';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Fallback for unknown types
|
|
201
|
+
return typeName || 'unknown';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Normalize a signature for comparison.
|
|
206
|
+
* Currently just returns the signature as-is, but can be extended
|
|
207
|
+
* to handle canonicalization if needed.
|
|
208
|
+
*/
|
|
209
|
+
export function normalizeSignature(signature: string): string {
|
|
210
|
+
return signature;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Compare two schemas for structural equivalence.
|
|
215
|
+
*/
|
|
216
|
+
export function areStructurallyEquivalent(schema1: ZodType, schema2: ZodType): boolean {
|
|
217
|
+
const sig1 = normalizeSignature(generateZodStructuralSignature(schema1));
|
|
218
|
+
const sig2 = normalizeSignature(generateZodStructuralSignature(schema2));
|
|
219
|
+
return sig1 === sig2;
|
|
220
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { generateZodStructuralSignature, normalizeSignature, areStructurallyEquivalent } from './TypeSignature';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { GraphVisitor } from "./GraphVisitor";
|
|
2
|
+
import { TypeNode, OperationNode, FieldNode, InputNode, ScalarNode } from "../graph/GraphNode";
|
|
3
|
+
import { weaveAllArchetypes, getAllArchetypeSchemas } from "../../core/ArcheType";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Visitor that pre-processes archetype schemas before other visitors run.
|
|
7
|
+
* Ensures all archetype GraphQL schemas are generated and cached.
|
|
8
|
+
*/
|
|
9
|
+
export class ArchetypePreprocessorVisitor extends GraphVisitor {
|
|
10
|
+
private processedArchetypes: Set<string> = new Set();
|
|
11
|
+
private archetypeSchemas: any[] = [];
|
|
12
|
+
|
|
13
|
+
override beforeVisit(): void {
|
|
14
|
+
// Pre-weave all archetypes to ensure schemas are generated
|
|
15
|
+
try {
|
|
16
|
+
const schema = weaveAllArchetypes();
|
|
17
|
+
if (schema) {
|
|
18
|
+
this.archetypeSchemas = getAllArchetypeSchemas();
|
|
19
|
+
}
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.warn("Failed to pre-process archetype schemas:", error);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
visitTypeNode(node: TypeNode): void {
|
|
26
|
+
// Check if this is an archetype type
|
|
27
|
+
if (node.metadata.isArchetype) {
|
|
28
|
+
this.processedArchetypes.add(node.name);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
visitOperationNode(node: OperationNode): void {
|
|
33
|
+
// Operations may reference archetype types
|
|
34
|
+
// We don't need to do anything special here
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
visitFieldNode(node: FieldNode): void {
|
|
38
|
+
// Fields may be part of archetype types
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
visitInputNode(node: InputNode): void {
|
|
42
|
+
// Input nodes don't need archetype preprocessing
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
visitScalarNode(node: ScalarNode): void {
|
|
46
|
+
// Scalar nodes don't need archetype preprocessing
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getResults(): {
|
|
50
|
+
processedArchetypes: string[];
|
|
51
|
+
archetypeSchemas: any[];
|
|
52
|
+
} {
|
|
53
|
+
return {
|
|
54
|
+
processedArchetypes: Array.from(this.processedArchetypes),
|
|
55
|
+
archetypeSchemas: [...this.archetypeSchemas]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check if an archetype has been processed
|
|
61
|
+
*/
|
|
62
|
+
isArchetypeProcessed(archetypeName: string): boolean {
|
|
63
|
+
return this.processedArchetypes.has(archetypeName);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get all processed archetype names
|
|
68
|
+
*/
|
|
69
|
+
getProcessedArchetypes(): string[] {
|
|
70
|
+
return Array.from(this.processedArchetypes);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Clear all processed data
|
|
75
|
+
*/
|
|
76
|
+
clear(): void {
|
|
77
|
+
this.processedArchetypes.clear();
|
|
78
|
+
this.archetypeSchemas = [];
|
|
79
|
+
}
|
|
80
|
+
}
|