@tdk-landscape/tdk-cli-core 1.3.34 → 1.3.37

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.
Files changed (53) hide show
  1. package/discovery/discovery_orchestrator.star +4 -4
  2. package/discovery/manifest/loading.star +9 -5
  3. package/discovery/manifest/normalize.star +3 -3
  4. package/discovery/manifest/validation.star +1 -1
  5. package/discovery/validation.star +1 -1
  6. package/dist/commands/doctor.d.ts.map +1 -1
  7. package/dist/commands/doctor.js +120 -6
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/resource.js +1 -1
  10. package/dist/commands/resource.js.map +1 -1
  11. package/dist/commands/up.d.ts.map +1 -1
  12. package/dist/commands/up.js +2 -48
  13. package/dist/commands/up.js.map +1 -1
  14. package/dist/types/index.d.ts +3 -1
  15. package/dist/types/index.d.ts.map +1 -1
  16. package/dist/types/index.js.map +1 -1
  17. package/dist/utils/constants.d.ts +1 -0
  18. package/dist/utils/constants.d.ts.map +1 -1
  19. package/dist/utils/constants.js +11 -0
  20. package/dist/utils/constants.js.map +1 -1
  21. package/dist/utils/resource-features.d.ts +1 -1
  22. package/dist/utils/resource-features.js +1 -1
  23. package/dist/utils/service-urls.d.ts +30 -0
  24. package/dist/utils/service-urls.d.ts.map +1 -0
  25. package/dist/utils/service-urls.js +93 -0
  26. package/dist/utils/service-urls.js.map +1 -0
  27. package/engine/REFACTORING_MANIFEST_SYSTEM.md +1 -1
  28. package/engine/lifecycle/orchestrator.star +4 -3
  29. package/engine/schemas/service-schema.json +5 -5
  30. package/engine/topologies/platform/docker/compose/compose.star +44 -24
  31. package/engine/topologies/platform/docker/constants.star +4 -0
  32. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +1 -0
  33. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +10 -1
  34. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +10 -6
  35. package/engine/topologies/platform/docker/networking/api_path_constants.star +30 -0
  36. package/engine/topologies/platform/docker/networking/traefik.star +2 -2
  37. package/engine/topologies/platform/net/AGENTS.md +64 -0
  38. package/engine/topologies/platform/security/AGENTS.md +49 -0
  39. package/engine/topologies/platform/state/AGENTS.md +64 -0
  40. package/engine/topologies/tilt/generators/vite/backend.star +1 -1
  41. package/engine/topologies/tilt/generators/vite/frontend.star +2 -1
  42. package/engine/topologies/tilt/generators/vite/helpers.star +1 -1
  43. package/engine/topologies/tilt/manifest/constants.star +2 -1
  44. package/engine/topologies/tilt/manifest/mapper.star +2 -2
  45. package/engine/topologies/tilt/manifest/parser.star +2 -2
  46. package/engine/topologies/tilt/manifest/schema.star +2 -2
  47. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +3 -3
  48. package/engine/topologies/tilt/manifest/tests/test_validator.star +2 -2
  49. package/engine/topologies/tilt/manifest/validator.star +7 -7
  50. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +3 -2
  51. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +3 -2
  52. package/package.json +1 -1
  53. package/engine/schemas/manifest-schema.json +0 -301
@@ -45,7 +45,7 @@ def generate_backend(manifest, write_fn=None):
45
45
 
46
46
  # 🔥 SMART DETECTION: Check if service uses vite-plugin-node for HMR
47
47
  use_vite_node = manifest.get('useViteNode', False)
48
- features = manifest.get('features', [])
48
+ features = manifest.get('featuresEnabled', [])
49
49
  if 'vite-node' in features or 'vite-plugin-node' in features or 'hmr' in features:
50
50
  use_vite_node = True
51
51
 
@@ -16,6 +16,7 @@ load('../../../../topologies/tilt/manifest/constants.star', 'GENERATED_CONFIG_FI
16
16
 
17
17
 
18
18
  load('./templates.star', 'TEMPLATE_VITE_FRONTEND', 'TEMPLATE_VITE_FRONTEND_BUILD')
19
+ load('../../../platform/docker/networking/api_path_constants.star', 'get_frontend_base_path')
19
20
  load('./helpers.star',
20
21
  'build_header',
21
22
  'generate_frontend_internal_aliases',
@@ -56,7 +57,7 @@ def generate_frontend(manifest, backend_manifest=None, write_fn=None):
56
57
  stack = manifest.get('stack', 'app')
57
58
  port = manifest.get('port', BASE_PORT_FRONTEND)
58
59
  hmr_port = port + 1000 # HMR port is always port + 1000
59
- base_path = manifest.get('basePath', '/' + stack + 's')
60
+ base_path = get_frontend_base_path(manifest)
60
61
  vite_base_path = _normalize_vite_base_path(base_path)
61
62
  app_name = manifest.get('appName', 'unknown')
62
63
 
@@ -164,7 +164,7 @@ def generate_backend_path_aliases(resource_path, manifest):
164
164
  # hand-edited features: ["ddd"] with no key at all is still blocked, but real
165
165
  # entitlement is checked client-side by hasDddLicense() in extension-fetch.ts
166
166
  # before the CLI ever writes "ddd" into a resource's features array.
167
- ddd_enabled = 'ddd' in manifest.get('features', []) and os.environ.get('TDK_LICENSE_KEY', '') != ''
167
+ ddd_enabled = 'ddd' in manifest.get('featuresEnabled', []) and os.environ.get('TDK_LICENSE_KEY', '') != ''
168
168
 
169
169
  if ddd_enabled:
170
170
  ddd_aliases = [
@@ -143,6 +143,7 @@ AUTOGENERATED_CONFIG_PATHS = {
143
143
  'bunfig': AUTOGENERATED_FOLDER + '/' + PACKAGE_CONFIG_FILENAMES['bunfig'],
144
144
  'bunfig_docker': AUTOGENERATED_FOLDER + '/' + PACKAGE_CONFIG_FILENAMES['bunfig_docker_autogenerated'],
145
145
  'prisma': AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['prisma'],
146
+ 'vite_build': AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_build'],
146
147
  }
147
148
 
148
149
  # =============================================================================
@@ -155,7 +156,7 @@ MANIFEST_DEFAULTS = {
155
156
  'healthCheckPath': HEALTH_CHECK_PATH,
156
157
  'internalPort': BASE_PORT_FRONTEND,
157
158
  'replicas': 1,
158
- 'features': [],
159
+ 'featuresEnabled': [],
159
160
  'envVars': {},
160
161
  'syncs': None,
161
162
  'dependsOn': [],
@@ -31,7 +31,7 @@ FIELD_MAPPINGS = {
31
31
  'replicas': ['replicas', 'instances', 'count', 'spec.replicas', 'metadata.replicas'],
32
32
 
33
33
  # Feature flags
34
- 'features': ['features', 'capabilities', 'spec.features', 'enabledFeatures', 'app.features'],
34
+ 'featuresEnabled': ['featuresEnabled', 'capabilities', 'spec.features', 'enabledFeatures', 'app.features'],
35
35
 
36
36
  # Dependencies
37
37
  'dependsOn': [
@@ -199,7 +199,7 @@ def _to_yaml_format(internal_manifest):
199
199
  'port': internal_manifest.get('port', 4000),
200
200
  'runtime': internal_manifest.get('runtime', 'bun'),
201
201
  'replicas': internal_manifest.get('replicas', 1),
202
- 'features': internal_manifest.get('features', []),
202
+ 'featuresEnabled': internal_manifest.get('featuresEnabled', []),
203
203
  'envVars': internal_manifest.get('envVars', {}),
204
204
  'dependencies': {
205
205
  'internal': internal_manifest.get('dependsOn', []),
@@ -90,7 +90,7 @@ def normalize(manifest, resource_path=""):
90
90
  # Copy all existing fields
91
91
  for key, value in manifest.items():
92
92
  normalized[key] = value
93
-
93
+
94
94
  # 1. Apply defaults for missing fields
95
95
  for field, default_value in MANIFEST_DEFAULTS.items():
96
96
  if field not in normalized or normalized[field] == None:
@@ -111,7 +111,7 @@ def normalize(manifest, resource_path=""):
111
111
  if normalized.get('syncs') == None or normalized.get('syncs') == []:
112
112
  normalized['syncs'] = _compute_syncs(
113
113
  normalized['appType'],
114
- normalized.get('features', [])
114
+ normalized.get('featuresEnabled', [])
115
115
  )
116
116
 
117
117
  # Compute backendName for frontends if missing
@@ -117,7 +117,7 @@ MANIFEST_SCHEMA = {
117
117
  'description': 'Runtime environment',
118
118
  'default': RUNTIME,
119
119
  },
120
- 'features': {
120
+ 'featuresEnabled': {
121
121
  'type': 'list',
122
122
  'required': False,
123
123
  'constraints': {
@@ -263,7 +263,7 @@ CROSS_FIELD_CONSTRAINTS = {
263
263
  'message': 'Frontend resources must specify backendName and basePath',
264
264
  },
265
265
  'prisma_requires_database': {
266
- 'condition': {'field': 'features', 'contains': 'prisma'},
266
+ 'condition': {'field': 'featuresEnabled', 'contains': 'prisma'},
267
267
  'requires': ['databaseName'],
268
268
  'severity': 'warning',
269
269
  'message': 'Resources using Prisma should specify databaseName',
@@ -281,7 +281,7 @@ def _test_manifest_merging():
281
281
  'appName': 'test-service',
282
282
  'appType': 'backend',
283
283
  'port': 4000,
284
- 'features': ['nats'],
284
+ 'featuresEnabled': ['nats'],
285
285
  'envVars': {
286
286
  'SHARED_VAR': 'base_value',
287
287
  },
@@ -289,7 +289,7 @@ def _test_manifest_merging():
289
289
 
290
290
  overlay = {
291
291
  'port': 4001, # Override
292
- 'features': ['prisma'], # Override
292
+ 'featuresEnabled': ['prisma'], # Override
293
293
  'envVars': {
294
294
  'SHARED_VAR': 'overlay_value', # Override
295
295
  'NEW_VAR': 'new_value', # Add
@@ -300,7 +300,7 @@ def _test_manifest_merging():
300
300
 
301
301
  assert_equal('test-service', merged['appName'], "Should preserve base appName")
302
302
  assert_equal(4001, merged['port'], "Should overlay port")
303
- assert_equal(['prisma'], merged['features'], "Should overlay features")
303
+ assert_equal(['prisma'], merged['featuresEnabled'], "Should overlay features")
304
304
  assert_equal('overlay_value', merged['envVars']['SHARED_VAR'], "Should overlay nested env var")
305
305
  assert_equal('new_value', merged['envVars']['NEW_VAR'], "Should add new env var")
306
306
 
@@ -213,7 +213,7 @@ def _test_cross_field_validation():
213
213
  'appType': 'backend',
214
214
  'stack': 'user',
215
215
  'port': 4001,
216
- 'features': ['prisma'],
216
+ 'featuresEnabled': ['prisma'],
217
217
  # Missing databaseName
218
218
  }
219
219
 
@@ -312,7 +312,7 @@ def _test_full_validation():
312
312
  'port': 4000,
313
313
  'replicas': 1,
314
314
  'runtime': 'bun',
315
- 'features': ['nats', 'prisma'],
315
+ 'featuresEnabled': ['nats', 'prisma'],
316
316
  'databaseName': 'TDK_user',
317
317
  'dependsOn': [],
318
318
  'traefik': {
@@ -316,16 +316,16 @@ def _validate_values(manifest, context):
316
316
  stats['rules_passed'] += 1
317
317
 
318
318
  # Validate features length
319
- if 'features' in manifest:
319
+ if 'featuresEnabled' in manifest:
320
320
  stats['fields_checked'] += 1
321
- features = manifest['features']
322
-
321
+ features = manifest.get('featuresEnabled', [])
322
+
323
323
  if len(features) > VALIDATION_THRESHOLDS['max_features']:
324
324
  warnings.append(ManifestErrors.new(
325
325
  message="Too many features ({}), consider splitting resource".format(len(features)),
326
326
  category=ManifestErrors.CATEGORY['VALIDATION'],
327
327
  severity=ManifestErrors.SEVERITY['WARNING'],
328
- context={'field': 'features', 'count': len(features), 'max': VALIDATION_THRESHOLDS['max_features']},
328
+ context={'field': 'featuresEnabled', 'count': len(features), 'max': VALIDATION_THRESHOLDS['max_features']},
329
329
  ))
330
330
  else:
331
331
  stats['rules_passed'] += 1
@@ -374,16 +374,16 @@ def _validate_cross_field(manifest, context):
374
374
  stats['rules_passed'] += 1
375
375
 
376
376
  # Prisma requires databaseName
377
- features = manifest.get('features', [])
377
+ features = manifest.get('featuresEnabled', [])
378
378
  if 'prisma' in features:
379
379
  stats['fields_checked'] += 1
380
-
380
+
381
381
  if not manifest.get('databaseName'):
382
382
  warnings.append(ManifestErrors.new(
383
383
  message="Resource with 'prisma' feature should specify databaseName",
384
384
  category=ManifestErrors.CATEGORY['VALIDATION'],
385
385
  severity=ManifestErrors.SEVERITY['WARNING'],
386
- context={'field': 'databaseName', 'features': features},
386
+ context={'field': 'databaseName', 'featuresEnabled': features},
387
387
  ))
388
388
  else:
389
389
  stats['rules_passed'] += 1
@@ -14,6 +14,7 @@ OUTPUT = struct(AUTOGENERATED_FOLDER = ".autogenerated")
14
14
 
15
15
  load("../../../../platform/docker/auth/auth.star", "AuthConfig")
16
16
  load("../../../../platform/docker/networking/traefik_helpers.star", "get_api_path")
17
+ load("../../../../platform/docker/networking/api_path_constants.star", "get_frontend_base_path")
17
18
  load("../../../../platform/docker/constants.star", "PlatformDockerConstants")
18
19
 
19
20
 
@@ -133,8 +134,8 @@ def _generate_params_env(manifest, backend_manifest=None, write_fn=None):
133
134
  backend_name = manifest.get('backendName', stack + '-backend')
134
135
  target_filename = '/' + OUTPUT.AUTOGENERATED_FOLDER + '/.env.frontend.autogenerated'
135
136
 
136
- # Get basePath from manifest or default to /{stack}
137
- base_path = manifest.get('basePath', '/' + stack)
137
+ # Shared with the Traefik route and the Vite `base` -- see get_frontend_base_path.
138
+ base_path = get_frontend_base_path(manifest)
138
139
  public_url = 'http://' + PlatformDockerConstants.LOCAL_DOMAIN + base_path
139
140
 
140
141
  content = """# Generated by Tilt SDK - DO NOT EDIT
@@ -360,7 +360,8 @@ def _generate_all_configs_for_resource(
360
360
  dockerfile_content = Docker.backend(
361
361
  res_path=resource_path,
362
362
  resource_name=resource_name,
363
- use_golden=should_enable('golden-image')
363
+ use_golden=should_enable('golden-image'),
364
+ use_prisma='prisma' in manifest.get('featuresEnabled', [])
364
365
  )
365
366
 
366
367
  write_file(resource_path + "/" + OUTPUT.AUTOGENERATED_FOLDER + "/" + GENERATED_CONFIG_FILENAMES['dockerfile_app'], dockerfile_content)
@@ -404,7 +405,7 @@ def _generate_all_configs_for_resource(
404
405
  TSConfig.frontend(resource_path, write_file, internal_deps=internal_deps_map, is_docker=False)
405
406
  else:
406
407
  prisma_path = resource_config.get('prisma_client_path', './node_modules/.prisma/client')
407
- resource_features = manifest.get('features', [])
408
+ resource_features = manifest.get('featuresEnabled', [])
408
409
  TSConfig.backend(resource_path, prisma_path, write_file, internal_deps=internal_deps_map, is_docker=True, features=resource_features)
409
410
  TSConfig.backend(resource_path, prisma_path, write_file, internal_deps=internal_deps_map, is_docker=False, features=resource_features)
410
411
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tdk-landscape/tdk-cli-core",
3
- "version": "1.3.34",
3
+ "version": "1.3.37",
4
4
  "description": "TDK CLI - Tilt Development Kit command line interface",
5
5
  "keywords": [
6
6
  "tdk",
@@ -1,301 +0,0 @@
1
- {
2
- "$id": "https://TDK Landscape.local/schemas/service-schema.json",
3
- "$schema": "http://json-schema.org/draft-07/schema#",
4
- "additionalProperties": false,
5
- "allOf": [
6
- {
7
- "if": {
8
- "properties": {
9
- "appType": {
10
- "const": "backend"
11
- }
12
- }
13
- },
14
- "then": {
15
- "properties": {
16
- "port": {
17
- "maximum": 5999,
18
- "minimum": 4000
19
- }
20
- },
21
- "required": [
22
- "port"
23
- ]
24
- }
25
- },
26
- {
27
- "if": {
28
- "properties": {
29
- "appType": {
30
- "const": "frontend"
31
- }
32
- }
33
- },
34
- "then": {
35
- "properties": {
36
- "port": {
37
- "maximum": 3999,
38
- "minimum": 3000
39
- }
40
- }
41
- }
42
- },
43
- {
44
- "if": {
45
- "properties": {
46
- "appType": {
47
- "const": "worker"
48
- }
49
- }
50
- },
51
- "then": {
52
- "properties": {
53
- "features": {
54
- "contains": {
55
- "const": "nats"
56
- }
57
- }
58
- },
59
- "required": [
60
- "features"
61
- ]
62
- }
63
- }
64
- ],
65
- "description": "Configuration for TDK Landscape services. Drives Tilt orchestration, Docker Compose generation, and dependency resolution. Supports both explicit service.json files and synthesis from directory structure (CEO Review: rename-service-manifest).",
66
- "properties": {
67
- "$schema": {
68
- "description": "JSON Schema reference for IDE validation",
69
- "type": "string"
70
- },
71
- "apiBasePath": {
72
- "default": "/api",
73
- "description": "Base path for API routes in Traefik.",
74
- "pattern": "^/[a-z0-9/-]*$",
75
- "type": "string"
76
- },
77
- "appName": {
78
- "description": "Unique resource identifier. Must match directory name.\nFormat: {stack}-{function}-{type}\nExample: 'user-management-backend'",
79
- "examples": [
80
- "user-management-backend",
81
- "order-planner-frontend"
82
- ],
83
- "maxLength": 64,
84
- "minLength": 3,
85
- "pattern": "^[a-z][a-z0-9-]*[a-z0-9]$",
86
- "type": "string"
87
- },
88
- "appType": {
89
- "description": "Service type determining build strategy:\n• backend - Hono/Express API server\n• frontend - Vite/React application\n• sdk - Shared library package\n• worker - Background job processor\n• migrator - Database migration runner\n• library - Shared code library",
90
- "enum": [
91
- "backend",
92
- "frontend",
93
- "sdk",
94
- "worker",
95
- "migrator",
96
- "library"
97
- ],
98
- "type": "string"
99
- },
100
- "backendName": {
101
- "description": "For frontends: associated backend service.\nAuto-computed from appName if not specified.",
102
- "pattern": "^[a-z][a-z0-9-]*-backend$",
103
- "type": "string"
104
- },
105
- "basePath": {
106
- "description": "Base path for frontend routing on TDK Landscape.localhost. Default is /{stack}s when omitted.",
107
- "pattern": "^/[a-z0-9/-]*$",
108
- "type": "string"
109
- },
110
- "databaseName": {
111
- "description": "Override auto-generated DB name.\nDefault: TDK_{stack}",
112
- "pattern": "^TDK_[a-z_]+$",
113
- "type": "string"
114
- },
115
- "dependencies": {
116
- "description": "Deprecated. Use 'dependsOn' instead.",
117
- "deprecated": true,
118
- "items": {
119
- "pattern": "^[a-z][a-z0-9-]*$",
120
- "type": "string"
121
- },
122
- "type": "array",
123
- "uniqueItems": true
124
- },
125
- "dockerfile": {
126
- "default": "autogenerated/Dockerfile.app.autogenerated",
127
- "description": "Custom Dockerfile path relative to service directory.",
128
- "type": "string"
129
- },
130
- "stack": {
131
- "description": "Technology stack this resource belongs to.\nUsed for:\n• Database naming: TDK_{stack}\n• Traefik routing: /api/{stack}\n• NATS queue: {stack}_backend_svc",
132
- "enum": [
133
- "user",
134
- "team",
135
- "auth",
136
- "order",
137
- "order-planner",
138
- "service",
139
- "inventory",
140
- "billing",
141
- "notification",
142
- "analytics",
143
- "profile",
144
- "platform",
145
- "product"
146
- ],
147
- "type": "string"
148
- },
149
- "params": {
150
- "additionalProperties": {
151
- "type": "string"
152
- },
153
- "description": "Non-secret configuration parameters (BASE_URL, PORT, etc.). Secrets use 'secrets' section below.",
154
- "type": "object"
155
- },
156
- "secrets": {
157
- "description": "Secret references resolved via Infisical. NO secrets in params/envVars - security violation.",
158
- "properties": {
159
- "required": {
160
- "description": "Required secret names. Service CRASHES if any are missing.",
161
- "items": {
162
- "type": "string",
163
- "pattern": "^[A-Z_][A-Z0-9_]*$"
164
- },
165
- "type": "array",
166
- "uniqueItems": true
167
- },
168
- "optional": {
169
- "description": "Optional secret names. Service starts but warns if missing.",
170
- "items": {
171
- "type": "string",
172
- "pattern": "^[A-Z_][A-Z0-9_]*$"
173
- },
174
- "type": "array",
175
- "uniqueItems": true
176
- }
177
- },
178
- "required": [
179
- "required"
180
- ],
181
- "type": "object"
182
- },
183
- "features": {
184
- "description": "Platform features:\n• nats - NATS JetStream eventing\n• prisma - DB ORM & migrations\n• redis - Caching layer\n• infisical - Secrets management\n• vitest - Test framework\n• traefik - Reverse proxy routing\n• websocket - WS support\n• graphql - GraphQL server\n• grpc - gRPC protocol",
185
- "items": {
186
- "enum": [
187
- "nats",
188
- "prisma",
189
- "redis",
190
- "infisical",
191
- "vitest",
192
- "traefik",
193
- "websocket",
194
- "graphql",
195
- "grpc"
196
- ],
197
- "type": "string"
198
- },
199
- "type": "array",
200
- "uniqueItems": true
201
- },
202
- "healthCheckPath": {
203
- "default": "/health",
204
- "description": "HTTP endpoint for health checks. Must return 200 OK.",
205
- "pattern": "^/[a-z0-9/-]*$",
206
- "type": "string"
207
- },
208
- "dependsOn": {
209
- "description": "Resource dependencies used for startup ordering and frontend dependency env generation.\nEach entry can be a stack name (e.g. 'user') or resource name (e.g. 'user-management-backend').\nServices listed here start before this service. Used for focus mode, health checks, migration order.",
210
- "items": {
211
- "pattern": "^[a-z][a-z0-9-]*$",
212
- "type": "string"
213
- },
214
- "type": "array",
215
- "uniqueItems": true
216
- },
217
- "nats": {
218
- "description": "Custom NATS configuration. Auto-generated if 'nats' in features.",
219
- "properties": {
220
- "queueGroup": {
221
- "description": "NATS queue group for load balancing",
222
- "type": "string"
223
- },
224
- "subjects": {
225
- "description": "NATS subjects to subscribe",
226
- "items": {
227
- "type": "string"
228
- },
229
- "type": "array"
230
- }
231
- },
232
- "type": "object"
233
- },
234
- "playwright": {
235
- "default": false,
236
- "description": "Generate Playwright config in .autogenerated/playwright.config.autogenerated.ts when true.",
237
- "type": "boolean"
238
- },
239
- "port": {
240
- "description": "External dev port (internal always 3000).\nAssignments:\n• 4000: user\n• 4001: service\n• 4002: order\n• 4003: team\n• 4004: auth",
241
- "examples": [
242
- 4000,
243
- 4001,
244
- 4002,
245
- 4003,
246
- 4004
247
- ],
248
- "maximum": 9999,
249
- "minimum": 3000,
250
- "type": "integer"
251
- },
252
- "replicas": {
253
- "default": 1,
254
- "description": "Container instances to run.\nCreates separate containers (service-1, service-2).\nTraefik load-balances between all replicas.",
255
- "maximum": 10,
256
- "minimum": 1,
257
- "type": "integer"
258
- },
259
- "runtime": {
260
- "default": "bun",
261
- "description": "Runtime to use for this service",
262
- "enum": [
263
- "node",
264
- "bun"
265
- ],
266
- "type": "string"
267
- },
268
- "syncs": {
269
- "description": "Directories to sync for hot-reload.\nAuto-computed if not specified:\n• backend: ['src', 'prisma']\n• frontend: ['src', 'public']",
270
- "items": {
271
- "type": "string"
272
- },
273
- "type": "array"
274
- },
275
- "traefik": {
276
- "description": "Custom Traefik routing. Auto-generated if not specified.",
277
- "properties": {
278
- "healthCheck": {
279
- "description": "Health check path for Traefik",
280
- "type": "string"
281
- },
282
- "host": {
283
- "description": "Custom hostname (e.g., 'user.backend.{project}.local')",
284
- "type": "string"
285
- },
286
- "pathPrefix": {
287
- "description": "URL path prefix (e.g., '/api/user')",
288
- "type": "string"
289
- }
290
- },
291
- "type": "object"
292
- }
293
- },
294
- "required": [
295
- "appName",
296
- "appType",
297
- "stack"
298
- ],
299
- "title": "TDK Landscape Service Configuration",
300
- "type": "object"
301
- }