@tamyla/clodo-framework 3.1.21 → 3.1.22
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/CHANGELOG.md +9 -0
- package/README.md +53 -0
- package/dist/bin/clodo-service.js +47 -15
- package/dist/bin/commands/deploy.js +115 -83
- package/dist/bin/commands/helpers/deployment-ui.js +138 -0
- package/dist/bin/commands/helpers/deployment-verification.js +251 -0
- package/dist/bin/commands/helpers/error-recovery.js +80 -0
- package/dist/bin/commands/helpers/resource-detection.js +113 -0
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/shared/cache/configuration-cache.js +82 -0
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/cloudflare/index.js +1 -1
- package/dist/bin/shared/cloudflare/ops.js +6 -4
- package/dist/bin/shared/config/ConfigurationManager.js +23 -1
- package/dist/bin/shared/config/command-config-manager.js +19 -3
- package/dist/bin/shared/config/index.js +1 -1
- package/dist/bin/shared/deployment/credential-collector.js +30 -7
- package/dist/bin/shared/deployment/index.js +2 -2
- package/dist/bin/shared/deployment/rollback-manager.js +4 -520
- package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
- package/dist/bin/shared/deployment/validator.js +40 -10
- package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
- package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
- package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
- package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
- package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
- package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
- package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
- package/dist/bin/shared/error-handling/error-classifier.js +46 -0
- package/dist/bin/shared/monitoring/health-checker.js +129 -1
- package/dist/bin/shared/monitoring/memory-manager.js +17 -6
- package/dist/bin/shared/routing/domain-router.js +1 -1
- package/dist/bin/shared/utils/deployment-validator.js +97 -0
- package/dist/bin/shared/utils/formatters.js +10 -0
- package/dist/bin/shared/utils/index.js +13 -1
- package/dist/bin/shared/utils/interactive-prompts.js +34 -18
- package/dist/bin/shared/utils/progress-manager.js +2 -2
- package/dist/bin/shared/utils/progress-spinner.js +53 -0
- package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/patterns/insecure-patterns.js +1 -1
- package/dist/utils/constants.js +102 -0
- package/dist/utils/deployment/wrangler-config-manager.js +215 -48
- package/dist/utils/framework-config.js +2 -2
- package/dist/utils/interactive-prompts.js +10 -59
- package/package.json +16 -8
- package/dist/bin/clodo-service-old.js +0 -868
- package/dist/bin/clodo-service-test.js +0 -10
- package/dist/bin/commands/assess.js +0 -91
- package/dist/bin/commands/create.js +0 -77
- package/dist/bin/commands/diagnose.js +0 -83
- package/dist/bin/commands/helpers.js +0 -138
- package/dist/bin/commands/update.js +0 -75
- package/dist/bin/database/deployment-db-manager.js +0 -423
- package/dist/bin/database/enterprise-db-manager.js +0 -457
- package/dist/bin/database/wrangler-d1-manager.js +0 -685
- package/dist/bin/deployment/enterprise-deploy.js +0 -877
- package/dist/bin/deployment/master-deploy.js +0 -1376
- package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
- package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
- package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
- package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
- package/dist/bin/deployment/modules/ValidationManager.js +0 -342
- package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/bin/deployment/test-interactive-utils.js +0 -66
- package/dist/bin/portfolio/portfolio-manager.js +0 -487
- package/dist/bin/service-management/create-service.js +0 -122
- package/dist/bin/service-management/init-service.js +0 -79
- package/dist/config/customers.js +0 -623
- package/dist/config/domains.js +0 -186
- package/dist/config/index.js +0 -6
- package/dist/database/database-orchestrator.js +0 -795
- package/dist/database/index.js +0 -4
- package/dist/deployment/index.js +0 -11
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/deployment/orchestration/index.js +0 -17
- package/dist/deployment/rollback-manager.js +0 -36
- package/dist/deployment/wrangler-deployer.js +0 -640
- package/dist/handlers/GenericRouteHandler.js +0 -532
- package/dist/migration/MigrationAdapters.js +0 -562
- package/dist/modules/ModuleManager.js +0 -668
- package/dist/modules/security.js +0 -96
- package/dist/orchestration/cross-domain-coordinator.js +0 -1083
- package/dist/orchestration/index.js +0 -5
- package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
- package/dist/orchestration/modules/DomainResolver.js +0 -198
- package/dist/orchestration/modules/StateManager.js +0 -332
- package/dist/orchestration/multi-domain-orchestrator.js +0 -724
- package/dist/routing/EnhancedRouter.js +0 -158
- package/dist/schema/SchemaManager.js +0 -778
- package/dist/service-management/ConfirmationEngine.js +0 -412
- package/dist/service-management/ErrorTracker.js +0 -299
- package/dist/service-management/GenerationEngine.js +0 -447
- package/dist/service-management/InputCollector.js +0 -619
- package/dist/service-management/ServiceCreator.js +0 -265
- package/dist/service-management/ServiceInitializer.js +0 -453
- package/dist/service-management/ServiceOrchestrator.js +0 -633
- package/dist/service-management/generators/BaseGenerator.js +0 -233
- package/dist/service-management/generators/GeneratorRegistry.js +0 -254
- package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
- package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
- package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
- package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
- package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
- package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
- package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
- package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
- package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
- package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
- package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
- package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
- package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
- package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
- package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
- package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
- package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
- package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
- package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
- package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
- package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
- package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
- package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
- package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
- package/dist/service-management/generators/utils/FileWriter.js +0 -179
- package/dist/service-management/generators/utils/PathResolver.js +0 -157
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
- package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
- package/dist/service-management/generators/utils/index.js +0 -18
- package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
- package/dist/service-management/handlers/GenerationHandler.js +0 -80
- package/dist/service-management/handlers/InputHandler.js +0 -59
- package/dist/service-management/handlers/ValidationHandler.js +0 -203
- package/dist/service-management/index.js +0 -14
- package/dist/service-management/routing/DomainRouteMapper.js +0 -311
- package/dist/service-management/routing/RouteGenerator.js +0 -266
- package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
- package/dist/service-management/routing/index.js +0 -14
- package/dist/service-management/services/DirectoryStructureService.js +0 -56
- package/dist/service-management/services/GenerationCoordinator.js +0 -208
- package/dist/service-management/services/GeneratorRegistry.js +0 -174
- package/dist/services/GenericDataService.js +0 -501
- package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
- package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
- package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
- package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
- package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
- package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
- package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
- package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
- package/dist/ui-structures/reference/service-manifest-template.json +0 -342
- package/dist/version/VersionDetector.js +0 -723
- package/dist/worker/index.js +0 -4
- package/dist/worker/integration.js +0 -351
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GeneratorRegistry - Manages generator instantiation and registration
|
|
3
|
-
*
|
|
4
|
-
* Provides centralized generator management with automatic instantiation,
|
|
5
|
-
* dependency injection, and configuration management.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { RouteGenerator } from '../routing/RouteGenerator.js';
|
|
9
|
-
import { PackageJsonGenerator } from '../generators/core/PackageJsonGenerator.js';
|
|
10
|
-
import { SiteConfigGenerator } from '../generators/core/SiteConfigGenerator.js';
|
|
11
|
-
import { WranglerTomlGenerator } from '../generators/config/WranglerTomlGenerator.js';
|
|
12
|
-
import { DomainsConfigGenerator } from '../generators/config/DomainsConfigGenerator.js';
|
|
13
|
-
import { WorkerIndexGenerator } from '../generators/code/WorkerIndexGenerator.js';
|
|
14
|
-
import { ServiceHandlersGenerator } from '../generators/code/ServiceHandlersGenerator.js';
|
|
15
|
-
import { ServiceMiddlewareGenerator } from '../generators/code/ServiceMiddlewareGenerator.js';
|
|
16
|
-
import { ServiceUtilsGenerator } from '../generators/code/ServiceUtilsGenerator.js';
|
|
17
|
-
import { EnvExampleGenerator } from '../generators/config/EnvExampleGenerator.js';
|
|
18
|
-
import { ProductionEnvGenerator } from '../generators/config/ProductionEnvGenerator.js';
|
|
19
|
-
import { StagingEnvGenerator } from '../generators/config/StagingEnvGenerator.js';
|
|
20
|
-
import { DevelopmentEnvGenerator } from '../generators/config/DevelopmentEnvGenerator.js';
|
|
21
|
-
import { ServiceSchemaGenerator } from '../generators/schemas/ServiceSchemaGenerator.js';
|
|
22
|
-
import { DeployScriptGenerator } from '../generators/scripts/DeployScriptGenerator.js';
|
|
23
|
-
import { SetupScriptGenerator } from '../generators/scripts/SetupScriptGenerator.js';
|
|
24
|
-
import { HealthCheckScriptGenerator } from '../generators/scripts/HealthCheckScriptGenerator.js';
|
|
25
|
-
import { UnitTestsGenerator } from '../generators/testing/UnitTestsGenerator.js';
|
|
26
|
-
import { IntegrationTestsGenerator } from '../generators/testing/IntegrationTestsGenerator.js';
|
|
27
|
-
import { JestConfigGenerator } from '../generators/testing/JestConfigGenerator.js';
|
|
28
|
-
import { EslintConfigGenerator } from '../generators/testing/EslintConfigGenerator.js';
|
|
29
|
-
import { ReadmeGenerator } from '../generators/documentation/ReadmeGenerator.js';
|
|
30
|
-
import { ApiDocsGenerator } from '../generators/documentation/ApiDocsGenerator.js';
|
|
31
|
-
import { DeploymentDocsGenerator } from '../generators/documentation/DeploymentDocsGenerator.js';
|
|
32
|
-
import { ConfigurationDocsGenerator } from '../generators/documentation/ConfigurationDocsGenerator.js';
|
|
33
|
-
import { CiWorkflowGenerator } from '../generators/cicd/CiWorkflowGenerator.js';
|
|
34
|
-
import { DeployWorkflowGenerator } from '../generators/cicd/DeployWorkflowGenerator.js';
|
|
35
|
-
import { GitignoreGenerator } from '../generators/tooling/GitignoreGenerator.js';
|
|
36
|
-
import { DockerComposeGenerator } from '../generators/tooling/DockerComposeGenerator.js';
|
|
37
|
-
import { StaticSiteGenerator } from '../generators/service-types/StaticSiteGenerator.js';
|
|
38
|
-
import { join } from 'path';
|
|
39
|
-
export class GeneratorRegistry {
|
|
40
|
-
constructor(options = {}) {
|
|
41
|
-
this.templatesDir = options.templatesDir;
|
|
42
|
-
this.outputDir = options.outputDir;
|
|
43
|
-
this.generators = new Map();
|
|
44
|
-
this.options = options;
|
|
45
|
-
this.initializeGenerators();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Initialize all generators with proper dependencies
|
|
50
|
-
*/
|
|
51
|
-
initializeGenerators() {
|
|
52
|
-
// Core generators (no dependencies)
|
|
53
|
-
this.register('routeGenerator', () => new RouteGenerator());
|
|
54
|
-
this.register('siteConfigGenerator', () => new SiteConfigGenerator({
|
|
55
|
-
templatesDir: this.templatesDir,
|
|
56
|
-
outputDir: this.outputDir
|
|
57
|
-
}));
|
|
58
|
-
|
|
59
|
-
// Service-type generators
|
|
60
|
-
this.register('staticSiteGenerator', () => new StaticSiteGenerator({
|
|
61
|
-
templatesDir: join(this.templatesDir, 'static-site'),
|
|
62
|
-
outputDir: this.outputDir
|
|
63
|
-
}));
|
|
64
|
-
|
|
65
|
-
// Generators with standard config
|
|
66
|
-
const standardGenerators = ['packageJsonGenerator', 'wranglerTomlGenerator', 'domainsConfigGenerator', 'workerIndexGenerator', 'serviceHandlersGenerator', 'serviceMiddlewareGenerator', 'serviceUtilsGenerator', 'envExampleGenerator', 'productionEnvGenerator', 'stagingEnvGenerator', 'developmentEnvGenerator', 'serviceSchemaGenerator', 'deployScriptGenerator', 'setupScriptGenerator', 'healthCheckScriptGenerator', 'unitTestsGenerator', 'integrationTestsGenerator', 'jestConfigGenerator', 'eslintConfigGenerator', 'readmeGenerator', 'apiDocsGenerator', 'deploymentDocsGenerator', 'configurationDocsGenerator', 'ciWorkflowGenerator', 'deployWorkflowGenerator', 'gitignoreGenerator', 'dockerComposeGenerator'];
|
|
67
|
-
standardGenerators.forEach(name => {
|
|
68
|
-
this.register(name, () => this.createStandardGenerator(name));
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Special generators with dependencies
|
|
72
|
-
this.register('wranglerTomlGenerator', () => new WranglerTomlGenerator({
|
|
73
|
-
templatesDir: this.templatesDir,
|
|
74
|
-
outputDir: this.outputDir,
|
|
75
|
-
routeGenerator: this.get('routeGenerator'),
|
|
76
|
-
siteConfigGenerator: this.get('siteConfigGenerator')
|
|
77
|
-
}));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Register a generator factory function
|
|
82
|
-
* @param {string} name - Generator name
|
|
83
|
-
* @param {Function} factory - Factory function that returns generator instance
|
|
84
|
-
*/
|
|
85
|
-
register(name, factory) {
|
|
86
|
-
this.generators.set(name, factory);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Get a generator instance (lazy instantiation)
|
|
91
|
-
* @param {string} name - Generator name
|
|
92
|
-
* @returns {*} - Generator instance
|
|
93
|
-
*/
|
|
94
|
-
get(name) {
|
|
95
|
-
const factory = this.generators.get(name);
|
|
96
|
-
if (!factory) {
|
|
97
|
-
throw new Error(`Generator '${name}' not registered`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// If factory returns a function, call it to get instance
|
|
101
|
-
const instance = typeof factory === 'function' ? factory() : factory;
|
|
102
|
-
|
|
103
|
-
// Cache the instance for future use
|
|
104
|
-
this.generators.set(name, instance);
|
|
105
|
-
return instance;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Create a standard generator with default configuration
|
|
110
|
-
* @param {string} className - Generator class name
|
|
111
|
-
* @returns {*} - Generator instance
|
|
112
|
-
*/
|
|
113
|
-
createStandardGenerator(className) {
|
|
114
|
-
// Convert camelCase to PascalCase for class name
|
|
115
|
-
const pascalCase = className.charAt(0).toUpperCase() + className.slice(1);
|
|
116
|
-
|
|
117
|
-
// Import all generator classes (this would be handled by the module system)
|
|
118
|
-
const generatorClasses = {
|
|
119
|
-
PackageJsonGenerator,
|
|
120
|
-
SiteConfigGenerator,
|
|
121
|
-
WranglerTomlGenerator,
|
|
122
|
-
DomainsConfigGenerator,
|
|
123
|
-
WorkerIndexGenerator,
|
|
124
|
-
ServiceHandlersGenerator,
|
|
125
|
-
ServiceMiddlewareGenerator,
|
|
126
|
-
ServiceUtilsGenerator,
|
|
127
|
-
EnvExampleGenerator,
|
|
128
|
-
ProductionEnvGenerator,
|
|
129
|
-
StagingEnvGenerator,
|
|
130
|
-
DevelopmentEnvGenerator,
|
|
131
|
-
ServiceSchemaGenerator,
|
|
132
|
-
DeployScriptGenerator,
|
|
133
|
-
SetupScriptGenerator,
|
|
134
|
-
HealthCheckScriptGenerator,
|
|
135
|
-
UnitTestsGenerator,
|
|
136
|
-
IntegrationTestsGenerator,
|
|
137
|
-
JestConfigGenerator,
|
|
138
|
-
EslintConfigGenerator,
|
|
139
|
-
ReadmeGenerator,
|
|
140
|
-
ApiDocsGenerator,
|
|
141
|
-
DeploymentDocsGenerator,
|
|
142
|
-
ConfigurationDocsGenerator,
|
|
143
|
-
CiWorkflowGenerator,
|
|
144
|
-
DeployWorkflowGenerator,
|
|
145
|
-
GitignoreGenerator,
|
|
146
|
-
DockerComposeGenerator
|
|
147
|
-
};
|
|
148
|
-
const GeneratorClass = generatorClasses[pascalCase];
|
|
149
|
-
if (!GeneratorClass) {
|
|
150
|
-
throw new Error(`Unknown generator class: ${pascalCase}`);
|
|
151
|
-
}
|
|
152
|
-
return new GeneratorClass({
|
|
153
|
-
templatesDir: this.templatesDir,
|
|
154
|
-
outputDir: this.outputDir
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Get all registered generator names
|
|
160
|
-
* @returns {string[]} - Array of generator names
|
|
161
|
-
*/
|
|
162
|
-
getRegisteredNames() {
|
|
163
|
-
return Array.from(this.generators.keys());
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Check if a generator is registered
|
|
168
|
-
* @param {string} name - Generator name
|
|
169
|
-
* @returns {boolean} - True if registered
|
|
170
|
-
*/
|
|
171
|
-
has(name) {
|
|
172
|
-
return this.generators.has(name);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
@@ -1,501 +0,0 @@
|
|
|
1
|
-
import { schemaManager } from '../schema/SchemaManager.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Generic Data Service
|
|
5
|
-
* Provides CRUD operations for any configured data model
|
|
6
|
-
*/
|
|
7
|
-
export class GenericDataService {
|
|
8
|
-
/**
|
|
9
|
-
* Create a new GenericDataService instance
|
|
10
|
-
* @param {Object} d1Client - D1 database client
|
|
11
|
-
* @param {string} modelName - Name of the model to work with
|
|
12
|
-
* @param {Object} options - Configuration options
|
|
13
|
-
*/
|
|
14
|
-
constructor(d1Client, modelName, options = {}) {
|
|
15
|
-
this.d1Client = d1Client;
|
|
16
|
-
this.modelName = modelName;
|
|
17
|
-
this.schema = schemaManager.getModel(modelName);
|
|
18
|
-
if (!this.schema) {
|
|
19
|
-
throw new Error(`Model '${modelName}' not registered. Use schemaManager.registerModel() first.`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Query result caching
|
|
23
|
-
this.queryCache = new Map();
|
|
24
|
-
this.cacheEnabled = options.cacheEnabled !== false;
|
|
25
|
-
this.defaultCacheTTL = options.defaultCacheTTL || 300; // 5 minutes default
|
|
26
|
-
|
|
27
|
-
// Security configuration
|
|
28
|
-
this.securityConfig = {
|
|
29
|
-
maxQueryLimit: options.maxQueryLimit || 1000,
|
|
30
|
-
// Maximum records per query
|
|
31
|
-
defaultQueryLimit: options.defaultQueryLimit || 100,
|
|
32
|
-
// Default records per query
|
|
33
|
-
maxBulkOperationSize: options.maxBulkOperationSize || 100,
|
|
34
|
-
// Maximum records in bulk operations
|
|
35
|
-
enablePagination: options.enablePagination !== false,
|
|
36
|
-
// Enable pagination by default
|
|
37
|
-
...options.securityConfig
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Generate cache key for query
|
|
43
|
-
* @param {string} operation - Operation type
|
|
44
|
-
* @param {Object} params - Query parameters
|
|
45
|
-
* @returns {string} Cache key
|
|
46
|
-
*/
|
|
47
|
-
generateCacheKey(operation, params = {}) {
|
|
48
|
-
return `${this.modelName}:${operation}:${JSON.stringify(params)}`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Get cached result if valid
|
|
53
|
-
* @param {string} cacheKey - Cache key
|
|
54
|
-
* @returns {any|null} Cached result or null if expired/not found
|
|
55
|
-
*/
|
|
56
|
-
getCachedResult(cacheKey) {
|
|
57
|
-
if (!this.cacheEnabled) return null;
|
|
58
|
-
const cached = this.queryCache.get(cacheKey);
|
|
59
|
-
if (!cached) return null;
|
|
60
|
-
|
|
61
|
-
// Check if expired
|
|
62
|
-
if (Date.now() > cached.expiresAt) {
|
|
63
|
-
this.queryCache.delete(cacheKey);
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
return cached.data;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Cache query result
|
|
71
|
-
* @param {string} cacheKey - Cache key
|
|
72
|
-
* @param {any} data - Data to cache
|
|
73
|
-
* @param {number} ttl - Time to live in seconds
|
|
74
|
-
*/
|
|
75
|
-
setCachedResult(cacheKey, data, ttl = null) {
|
|
76
|
-
if (!this.cacheEnabled) return;
|
|
77
|
-
const expiresAt = Date.now() + (ttl || this.defaultCacheTTL) * 1000;
|
|
78
|
-
this.queryCache.set(cacheKey, {
|
|
79
|
-
data,
|
|
80
|
-
expiresAt,
|
|
81
|
-
createdAt: Date.now()
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Clear cache for specific operations or all cache
|
|
87
|
-
* @param {string} operation - Optional: specific operation to clear
|
|
88
|
-
* @param {Object} params - Optional: specific parameters to clear
|
|
89
|
-
*/
|
|
90
|
-
clearCache(operation = null, params = null) {
|
|
91
|
-
if (operation && params) {
|
|
92
|
-
// Clear specific cache entry
|
|
93
|
-
const cacheKey = this.generateCacheKey(operation, params);
|
|
94
|
-
this.queryCache.delete(cacheKey);
|
|
95
|
-
} else if (operation) {
|
|
96
|
-
// Clear all cache entries for an operation
|
|
97
|
-
for (const [key] of this.queryCache) {
|
|
98
|
-
if (key.startsWith(`${this.modelName}:${operation}:`)) {
|
|
99
|
-
this.queryCache.delete(key);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
// Clear all cache
|
|
104
|
-
this.queryCache.clear();
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Format validation errors into a human-readable message
|
|
110
|
-
* @param {Array} errors - Validation errors array
|
|
111
|
-
* @returns {string} Formatted error message
|
|
112
|
-
*/
|
|
113
|
-
formatValidationErrors(errors = []) {
|
|
114
|
-
if (!Array.isArray(errors) || errors.length === 0) {
|
|
115
|
-
return 'Unknown validation error';
|
|
116
|
-
}
|
|
117
|
-
const details = errors.map(error => {
|
|
118
|
-
if (!error) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
if (typeof error === 'string') {
|
|
122
|
-
return error;
|
|
123
|
-
}
|
|
124
|
-
if (Array.isArray(error)) {
|
|
125
|
-
return error.map(item => this.formatValidationErrors([item])).join('; ');
|
|
126
|
-
}
|
|
127
|
-
if (typeof error === 'object') {
|
|
128
|
-
const field = error.field || error.path || error.name || 'field';
|
|
129
|
-
if (error.message) {
|
|
130
|
-
return `${field}: ${error.message}`;
|
|
131
|
-
}
|
|
132
|
-
if (error.reason) {
|
|
133
|
-
return `${field}: ${error.reason}`;
|
|
134
|
-
}
|
|
135
|
-
if (error.code) {
|
|
136
|
-
return `${field}: ${error.code}`;
|
|
137
|
-
}
|
|
138
|
-
try {
|
|
139
|
-
return `${field}: ${JSON.stringify(error)}`;
|
|
140
|
-
} catch {
|
|
141
|
-
return `${field}: ${String(error)}`;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return String(error);
|
|
145
|
-
}).filter(Boolean);
|
|
146
|
-
return details.length > 0 ? details.join('; ') : 'Unknown validation error';
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Create a new record
|
|
151
|
-
* @param {Object} data - Record data
|
|
152
|
-
* @returns {Promise<Object>} Created record
|
|
153
|
-
*/
|
|
154
|
-
async create(data) {
|
|
155
|
-
// Validate data
|
|
156
|
-
const validation = schemaManager.validateData(this.modelName, data);
|
|
157
|
-
if (!validation.valid) {
|
|
158
|
-
const errorMessage = this.formatValidationErrors(validation.errors);
|
|
159
|
-
throw new Error(`Validation failed: ${errorMessage}`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Generate ID if not provided
|
|
163
|
-
const recordData = {
|
|
164
|
-
...validation.data
|
|
165
|
-
};
|
|
166
|
-
if (!recordData.id) {
|
|
167
|
-
recordData.id = this.d1Client.generateId();
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Set timestamps
|
|
171
|
-
const now = this.d1Client.getCurrentTimestamp();
|
|
172
|
-
if (this.schema.columns.created_at && !recordData.created_at) {
|
|
173
|
-
recordData.created_at = now;
|
|
174
|
-
}
|
|
175
|
-
if (this.schema.columns.updated_at && !recordData.updated_at) {
|
|
176
|
-
recordData.updated_at = now;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Generate SQL
|
|
180
|
-
const {
|
|
181
|
-
sql,
|
|
182
|
-
params
|
|
183
|
-
} = schemaManager.generateSQL(this.modelName, 'create', recordData);
|
|
184
|
-
|
|
185
|
-
// Execute
|
|
186
|
-
const result = await this.d1Client.run(sql, params);
|
|
187
|
-
if (result.success) {
|
|
188
|
-
// Clear relevant caches after successful creation
|
|
189
|
-
this.clearCache('findAll');
|
|
190
|
-
return {
|
|
191
|
-
...recordData,
|
|
192
|
-
id: recordData.id
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
throw new Error('Failed to create record');
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Find records by criteria with advanced options
|
|
200
|
-
* @param {Object} criteria - Search criteria
|
|
201
|
-
* @param {Array} include - Relationships to include
|
|
202
|
-
* @param {Array} fields - Fields to select
|
|
203
|
-
* @returns {Promise<Array>} Found records
|
|
204
|
-
*/
|
|
205
|
-
async find(criteria = {}, include = [], fields = null) {
|
|
206
|
-
const cacheKey = this.generateCacheKey('find', {
|
|
207
|
-
criteria,
|
|
208
|
-
include,
|
|
209
|
-
fields
|
|
210
|
-
});
|
|
211
|
-
const cached = this.getCachedResult(cacheKey);
|
|
212
|
-
if (cached !== null) {
|
|
213
|
-
return cached;
|
|
214
|
-
}
|
|
215
|
-
let result;
|
|
216
|
-
if (include.length > 0) {
|
|
217
|
-
result = await this.findWithRelations(criteria, include, fields);
|
|
218
|
-
} else {
|
|
219
|
-
const {
|
|
220
|
-
sql,
|
|
221
|
-
params
|
|
222
|
-
} = schemaManager.generateSQL(this.modelName, 'read', {
|
|
223
|
-
where: criteria,
|
|
224
|
-
fields
|
|
225
|
-
});
|
|
226
|
-
result = await this.d1Client.all(sql, params);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Cache the result
|
|
230
|
-
this.setCachedResult(cacheKey, result);
|
|
231
|
-
return result;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Find all records with pagination and security limits
|
|
236
|
-
* @param {Object} options - Query options
|
|
237
|
-
* @param {number} options.limit - Maximum records to return (default: configured default, max: configured max)
|
|
238
|
-
* @param {number} options.offset - Number of records to skip (default: 0)
|
|
239
|
-
* @param {Object} options.orderBy - Sort options {field: 'asc'|'desc'}
|
|
240
|
-
* @param {Object} options.where - Filter criteria
|
|
241
|
-
* @returns {Promise<Object>} Paginated result with data, total, limit, offset
|
|
242
|
-
*/
|
|
243
|
-
async findAll(options = {}) {
|
|
244
|
-
// Apply security limits
|
|
245
|
-
const requestedLimit = options.limit !== undefined ? options.limit : this.securityConfig.defaultQueryLimit;
|
|
246
|
-
|
|
247
|
-
// Validate limit before applying security constraints
|
|
248
|
-
if (requestedLimit <= 0) {
|
|
249
|
-
throw new Error(`Invalid limit: ${requestedLimit}. Must be positive.`);
|
|
250
|
-
}
|
|
251
|
-
const limit = Math.min(requestedLimit, this.securityConfig.maxQueryLimit);
|
|
252
|
-
const offset = Math.max(0, options.offset || 0);
|
|
253
|
-
|
|
254
|
-
// Prevent excessive offset (basic protection against very large offsets)
|
|
255
|
-
if (offset > 1000000) {
|
|
256
|
-
throw new Error(`Offset too large: ${offset}. Maximum allowed offset is 1,000,000.`);
|
|
257
|
-
}
|
|
258
|
-
const queryOptions = {
|
|
259
|
-
limit,
|
|
260
|
-
offset,
|
|
261
|
-
orderBy: options.orderBy,
|
|
262
|
-
where: options.where || {}
|
|
263
|
-
};
|
|
264
|
-
const cacheKey = this.generateCacheKey('findAll', queryOptions);
|
|
265
|
-
const cached = this.getCachedResult(cacheKey);
|
|
266
|
-
if (cached !== null) {
|
|
267
|
-
return cached;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Build query with pagination
|
|
271
|
-
let sql = `SELECT * FROM ${this.schema.tableName}`;
|
|
272
|
-
let params = [];
|
|
273
|
-
|
|
274
|
-
// Add WHERE clause if provided
|
|
275
|
-
if (options.where && Object.keys(options.where).length > 0) {
|
|
276
|
-
const conditions = [];
|
|
277
|
-
Object.entries(options.where).forEach(([key, value]) => {
|
|
278
|
-
conditions.push(`${key} = ?`);
|
|
279
|
-
params.push(value);
|
|
280
|
-
});
|
|
281
|
-
sql += ` WHERE ${conditions.join(' AND ')}`;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Add ORDER BY if provided
|
|
285
|
-
if (options.orderBy && Object.keys(options.orderBy).length > 0) {
|
|
286
|
-
const orderClauses = [];
|
|
287
|
-
Object.entries(options.orderBy).forEach(([field, direction]) => {
|
|
288
|
-
orderClauses.push(`${field} ${direction.toUpperCase()}`);
|
|
289
|
-
});
|
|
290
|
-
sql += ` ORDER BY ${orderClauses.join(', ')}`;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Add LIMIT and OFFSET
|
|
294
|
-
sql += ` LIMIT ? OFFSET ?`;
|
|
295
|
-
params.push(limit, offset);
|
|
296
|
-
|
|
297
|
-
// Execute the query
|
|
298
|
-
const data = await this.d1Client.all(sql, params);
|
|
299
|
-
|
|
300
|
-
// Get total count for pagination (without LIMIT/OFFSET)
|
|
301
|
-
let countSql = `SELECT COUNT(*) as total FROM ${this.schema.tableName}`;
|
|
302
|
-
let countParams = [];
|
|
303
|
-
if (options.where && Object.keys(options.where).length > 0) {
|
|
304
|
-
const conditions = [];
|
|
305
|
-
Object.entries(options.where).forEach(([key, value]) => {
|
|
306
|
-
conditions.push(`${key} = ?`);
|
|
307
|
-
countParams.push(value);
|
|
308
|
-
});
|
|
309
|
-
countSql += ` WHERE ${conditions.join(' AND ')}`;
|
|
310
|
-
}
|
|
311
|
-
const countResult = await this.d1Client.first(countSql, countParams);
|
|
312
|
-
const total = countResult ? countResult.total : 0;
|
|
313
|
-
const result = {
|
|
314
|
-
data,
|
|
315
|
-
pagination: {
|
|
316
|
-
total,
|
|
317
|
-
limit,
|
|
318
|
-
offset,
|
|
319
|
-
hasNext: offset + limit < total,
|
|
320
|
-
hasPrev: offset > 0,
|
|
321
|
-
totalPages: Math.ceil(total / limit),
|
|
322
|
-
currentPage: Math.floor(offset / limit) + 1
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
// Cache the result
|
|
327
|
-
this.setCachedResult(cacheKey, result);
|
|
328
|
-
return result;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Update a record
|
|
333
|
-
* @param {string} id - Record ID
|
|
334
|
-
* @param {Object} updates - Fields to update
|
|
335
|
-
* @returns {Promise<Object>} Updated record
|
|
336
|
-
*/
|
|
337
|
-
async update(id, updates) {
|
|
338
|
-
// Validate updates
|
|
339
|
-
const validation = schemaManager.validateData(this.modelName, updates);
|
|
340
|
-
if (!validation.valid) {
|
|
341
|
-
const errorMessage = this.formatValidationErrors(validation.errors);
|
|
342
|
-
throw new Error(`Validation failed: ${errorMessage}`);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Set updated timestamp
|
|
346
|
-
const updateData = {
|
|
347
|
-
...validation.data,
|
|
348
|
-
id
|
|
349
|
-
};
|
|
350
|
-
if (this.schema.columns.updated_at) {
|
|
351
|
-
updateData.updated_at = this.d1Client.getCurrentTimestamp();
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Generate SQL
|
|
355
|
-
const {
|
|
356
|
-
sql,
|
|
357
|
-
params
|
|
358
|
-
} = schemaManager.generateSQL(this.modelName, 'update', updateData);
|
|
359
|
-
|
|
360
|
-
// Execute
|
|
361
|
-
const result = await this.d1Client.run(sql, params);
|
|
362
|
-
if (result.success) {
|
|
363
|
-
// Return updated record
|
|
364
|
-
return await this.findById(id);
|
|
365
|
-
} else {
|
|
366
|
-
throw new Error('Failed to update record');
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Delete a record
|
|
372
|
-
* @param {string} id - Record ID
|
|
373
|
-
* @returns {Promise<boolean>} Success status
|
|
374
|
-
*/
|
|
375
|
-
async delete(id) {
|
|
376
|
-
const {
|
|
377
|
-
sql,
|
|
378
|
-
params
|
|
379
|
-
} = schemaManager.generateSQL(this.modelName, 'delete', {
|
|
380
|
-
id
|
|
381
|
-
});
|
|
382
|
-
const result = await this.d1Client.run(sql, params);
|
|
383
|
-
return result.success;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Count records matching criteria
|
|
388
|
-
* @param {Object} criteria - Count criteria
|
|
389
|
-
* @returns {Promise<number>} Record count
|
|
390
|
-
*/
|
|
391
|
-
async count(criteria = {}) {
|
|
392
|
-
let sql = `SELECT COUNT(*) as count FROM ${this.schema.tableName}`;
|
|
393
|
-
let params = [];
|
|
394
|
-
if (Object.keys(criteria).length > 0) {
|
|
395
|
-
const conditions = [];
|
|
396
|
-
Object.entries(criteria).forEach(([key, value]) => {
|
|
397
|
-
conditions.push(`${key} = ?`);
|
|
398
|
-
params.push(value);
|
|
399
|
-
});
|
|
400
|
-
sql += ` WHERE ${conditions.join(' AND ')}`;
|
|
401
|
-
}
|
|
402
|
-
const result = await this.d1Client.first(sql, params);
|
|
403
|
-
return result?.count || 0;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Check if record exists
|
|
408
|
-
* @param {string} id - Record ID
|
|
409
|
-
* @returns {Promise<boolean>} Existence status
|
|
410
|
-
*/
|
|
411
|
-
async exists(id) {
|
|
412
|
-
const record = await this.findById(id);
|
|
413
|
-
return !!record;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Get paginated results
|
|
418
|
-
* @param {Object} criteria - Search criteria
|
|
419
|
-
* @param {Object} pagination - Pagination options
|
|
420
|
-
* @returns {Promise<Object>} Paginated results
|
|
421
|
-
*/
|
|
422
|
-
async paginate(criteria = {}, pagination = {}) {
|
|
423
|
-
const {
|
|
424
|
-
page = 1,
|
|
425
|
-
limit = 10
|
|
426
|
-
} = pagination;
|
|
427
|
-
const offset = (page - 1) * limit;
|
|
428
|
-
|
|
429
|
-
// Get total count
|
|
430
|
-
const total = await this.count(criteria);
|
|
431
|
-
|
|
432
|
-
// Get paginated results
|
|
433
|
-
let sql = `SELECT * FROM ${this.schema.tableName}`;
|
|
434
|
-
let params = [];
|
|
435
|
-
if (Object.keys(criteria).length > 0) {
|
|
436
|
-
const conditions = [];
|
|
437
|
-
Object.entries(criteria).forEach(([key, value]) => {
|
|
438
|
-
conditions.push(`${key} = ?`);
|
|
439
|
-
params.push(value);
|
|
440
|
-
});
|
|
441
|
-
sql += ` WHERE ${conditions.join(' AND ')}`;
|
|
442
|
-
}
|
|
443
|
-
sql += ` LIMIT ? OFFSET ?`;
|
|
444
|
-
params.push(limit, offset);
|
|
445
|
-
const records = await this.d1Client.all(sql, params);
|
|
446
|
-
return {
|
|
447
|
-
data: records,
|
|
448
|
-
pagination: {
|
|
449
|
-
page,
|
|
450
|
-
limit,
|
|
451
|
-
total,
|
|
452
|
-
totalPages: Math.ceil(total / limit),
|
|
453
|
-
hasNext: page * limit < total,
|
|
454
|
-
hasPrev: page > 1
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Find a single record by ID
|
|
461
|
-
* @param {string|number} id - Record ID
|
|
462
|
-
* @param {Array} include - Relations to include
|
|
463
|
-
* @param {Array} fields - Fields to select
|
|
464
|
-
* @returns {Promise<Object|null>} Found record or null
|
|
465
|
-
*/
|
|
466
|
-
async findById(id, include = [], fields = null) {
|
|
467
|
-
const result = await this.find({
|
|
468
|
-
id
|
|
469
|
-
}, include, fields);
|
|
470
|
-
return result && result.length > 0 ? result[0] : null;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Factory function to create a data service for a model
|
|
476
|
-
* @param {Object} d1Client - D1 database client
|
|
477
|
-
* @param {string} modelName - Name of the model
|
|
478
|
-
* @returns {GenericDataService} Data service instance
|
|
479
|
-
*/
|
|
480
|
-
/**
|
|
481
|
-
* Factory function to create a data service for a model
|
|
482
|
-
* @param {Object} d1Client - D1 database client
|
|
483
|
-
* @param {string} modelName - Name of the model
|
|
484
|
-
* @returns {GenericDataService} Data service instance
|
|
485
|
-
*/
|
|
486
|
-
export function createDataService(d1Client, modelName) {
|
|
487
|
-
return new GenericDataService(d1Client, modelName);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
/**
|
|
491
|
-
* Get all available data services
|
|
492
|
-
* @returns {Object} Map of model names to service instances
|
|
493
|
-
*/
|
|
494
|
-
export function getAllDataServices() {
|
|
495
|
-
const services = {};
|
|
496
|
-
// Note: This would require schemaManager to have getAllModels method
|
|
497
|
-
// for (const [modelName] of schemaManager.getAllModels()) {
|
|
498
|
-
// services[modelName] = new GenericDataService(d1Client, modelName);
|
|
499
|
-
// }
|
|
500
|
-
return services;
|
|
501
|
-
}
|