@crossdelta/platform-sdk 0.13.0 → 0.13.2

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 (63) hide show
  1. package/README.md +8 -6
  2. package/package.json +1 -1
  3. package/bin/cli.js +0 -312
  4. package/bin/docs/generators/README.md +0 -56
  5. package/bin/docs/generators/code-style.md +0 -96
  6. package/bin/docs/generators/hono-bun.md +0 -181
  7. package/bin/docs/generators/hono-node.md +0 -194
  8. package/bin/docs/generators/nest.md +0 -358
  9. package/bin/docs/generators/service.md +0 -564
  10. package/bin/docs/generators/testing.md +0 -97
  11. package/bin/integration.collection.json +0 -18
  12. package/bin/templates/hono-microservice/Dockerfile.hbs +0 -16
  13. package/bin/templates/hono-microservice/biome.json.hbs +0 -3
  14. package/bin/templates/hono-microservice/src/index.ts.hbs +0 -18
  15. package/bin/templates/hono-microservice/tsconfig.json.hbs +0 -14
  16. package/bin/templates/nest-microservice/Dockerfile.hbs +0 -37
  17. package/bin/templates/nest-microservice/biome.json.hbs +0 -3
  18. package/bin/templates/nest-microservice/src/app.context.ts.hbs +0 -17
  19. package/bin/templates/nest-microservice/src/events/events.module.ts.hbs +0 -8
  20. package/bin/templates/nest-microservice/src/events/events.service.ts.hbs +0 -22
  21. package/bin/templates/nest-microservice/src/main.ts.hbs +0 -34
  22. package/bin/templates/workspace/.github/README.md +0 -70
  23. package/bin/templates/workspace/.github/actions/check-image-tag-exists/action.yml +0 -27
  24. package/bin/templates/workspace/.github/actions/check-image-tag-exists/index.js +0 -179
  25. package/bin/templates/workspace/.github/actions/check-path-changes/action.yml +0 -21
  26. package/bin/templates/workspace/.github/actions/check-path-changes/index.js +0 -192
  27. package/bin/templates/workspace/.github/actions/detect-skipped-services/action.yml +0 -38
  28. package/bin/templates/workspace/.github/actions/generate-scope-matrix/action.yml +0 -17
  29. package/bin/templates/workspace/.github/actions/generate-scope-matrix/index.js +0 -355
  30. package/bin/templates/workspace/.github/actions/prepare-build-context/action.yml +0 -49
  31. package/bin/templates/workspace/.github/actions/resolve-scope-tags/action.yml +0 -31
  32. package/bin/templates/workspace/.github/actions/resolve-scope-tags/index.js +0 -398
  33. package/bin/templates/workspace/.github/actions/setup-bun-install/action.yml.hbs +0 -57
  34. package/bin/templates/workspace/.github/copilot-chat-configuration.json +0 -49
  35. package/bin/templates/workspace/.github/copilot-instructions.md.hbs +0 -72
  36. package/bin/templates/workspace/.github/dependabot.yml +0 -18
  37. package/bin/templates/workspace/.github/workflows/build-and-deploy.yml.hbs +0 -231
  38. package/bin/templates/workspace/.github/workflows/lint-and-tests.yml.hbs +0 -32
  39. package/bin/templates/workspace/.github/workflows/publish-packages.yml +0 -155
  40. package/bin/templates/workspace/apps/.gitkeep +0 -0
  41. package/bin/templates/workspace/biome.json.hbs +0 -62
  42. package/bin/templates/workspace/bunfig.toml.hbs +0 -5
  43. package/bin/templates/workspace/docs/.gitkeep +0 -0
  44. package/bin/templates/workspace/editorconfig.hbs +0 -9
  45. package/bin/templates/workspace/gitignore.hbs +0 -15
  46. package/bin/templates/workspace/infra/Pulumi.dev.yaml.hbs +0 -5
  47. package/bin/templates/workspace/infra/Pulumi.yaml.hbs +0 -6
  48. package/bin/templates/workspace/infra/index.ts.hbs +0 -56
  49. package/bin/templates/workspace/infra/package.json.hbs +0 -21
  50. package/bin/templates/workspace/infra/services/.gitkeep +0 -0
  51. package/bin/templates/workspace/infra/tsconfig.json.hbs +0 -15
  52. package/bin/templates/workspace/npmrc.hbs +0 -2
  53. package/bin/templates/workspace/package.json.hbs +0 -51
  54. package/bin/templates/workspace/packages/.gitkeep +0 -0
  55. package/bin/templates/workspace/packages/contracts/README.md.hbs +0 -166
  56. package/bin/templates/workspace/packages/contracts/package.json.hbs +0 -22
  57. package/bin/templates/workspace/packages/contracts/src/events/index.ts +0 -16
  58. package/bin/templates/workspace/packages/contracts/src/index.ts +0 -10
  59. package/bin/templates/workspace/packages/contracts/src/stream-policies.ts.hbs +0 -40
  60. package/bin/templates/workspace/packages/contracts/tsconfig.json.hbs +0 -7
  61. package/bin/templates/workspace/pnpm-workspace.yaml.hbs +0 -5
  62. package/bin/templates/workspace/turbo.json +0 -37
  63. package/bin/templates/workspace/turbo.json.hbs +0 -29
@@ -1,398 +0,0 @@
1
- /**
2
- * Resolve Scope Tags Action
3
- *
4
- * Determines the correct Docker image tags for all deployable scopes.
5
- * Uses change markers from the build job when available, falls back to
6
- * querying GHCR for the latest tag if a scope wasn't rebuilt.
7
- *
8
- * This action bridges the build and deploy jobs by mapping scope short names
9
- * to their checksum-based image tags.
10
- *
11
- * @example
12
- * - uses: ./.github/actions/resolve-scope-tags
13
- * with:
14
- * markers-dir: .artifacts
15
- * scope-roots: apps,services
16
- * github-token: ${{ secrets.GITHUB_TOKEN }}
17
- *
18
- * @outputs scope_image_tags - JSON map of shortName -> checksum tag
19
- */
20
-
21
- const { appendFileSync } = require('node:fs')
22
- const { existsSync, lstatSync, readdirSync, readFileSync, statSync } = require('node:fs')
23
- const { basename, join, relative } = require('node:path')
24
-
25
- // Default directories to scan for scopes (Turborepo convention)
26
- const defaultRoots = ['apps', 'services']
27
-
28
- // Prefix for change marker artifact directories
29
- const changedMarkerPrefix = 'changed-'
30
-
31
- // Maximum pages to check when querying GHCR for existing tags
32
- const maxPages = Number(process.env.TAG_CHECK_MAX_PAGES ?? '10')
33
-
34
- // ============================================================================
35
- // Input/Output Helpers
36
- // ============================================================================
37
-
38
- /**
39
- * Builds environment variable keys for GitHub Actions inputs.
40
- * @param {string} name - Input name
41
- * @returns {string[]} Possible environment variable keys
42
- */
43
- const buildInputKeys = (name) => {
44
- const trimmed = name.trim()
45
- const upper = trimmed.toUpperCase()
46
- const normalized = upper.replace(/[^A-Z0-9]+/g, '_')
47
- return Array.from(new Set([`INPUT_${upper}`, `INPUT_${normalized}`]))
48
- }
49
-
50
- /**
51
- * Retrieves a GitHub Actions input value from environment variables.
52
- * @param {string} name - Input name as defined in action.yml
53
- * @param {Object} options - Options
54
- * @param {boolean} [options.required=false] - Whether the input is required
55
- * @param {string} [options.defaultValue=''] - Default value if input is not set
56
- * @returns {string} The input value
57
- */
58
- const getInput = (name, { required = false, defaultValue = '' } = {}) => {
59
- const keys = buildInputKeys(name)
60
- const raw = keys.map((key) => process.env[key]).find((value) => typeof value === 'string')
61
- const value = typeof raw === 'string' ? raw.trim() : defaultValue
62
-
63
- if (required && !value) {
64
- console.error(`Input "${name}" is required`)
65
- process.exit(1)
66
- }
67
-
68
- return value
69
- }
70
-
71
- /**
72
- * Sets a GitHub Actions output value.
73
- * @param {string} name - Output name
74
- * @param {string} value - Output value
75
- */
76
- const setOutput = (name, value) => {
77
- const outputFile = process.env.GITHUB_OUTPUT
78
- if (outputFile) {
79
- appendFileSync(outputFile, `${name}=${value}\n`)
80
- } else {
81
- console.log(`${name}=${value}`)
82
- }
83
- }
84
-
85
- /**
86
- * Parses a comma/space separated list into an array.
87
- * @param {string} value - Input string
88
- * @returns {string[]} Array of parsed items
89
- */
90
- const parseList = (value) => {
91
- if (!value) return []
92
-
93
- return value
94
- .split(/[^a-zA-Z0-9._-]+/)
95
- .map((entry) => entry.trim())
96
- .filter(Boolean)
97
- }
98
-
99
- // ============================================================================
100
- // Configuration
101
- // ============================================================================
102
-
103
- const markersDir = getInput('markers-dir', { defaultValue: '.artifacts' })
104
- const scopeRoots = parseList(getInput('scope-roots'))
105
- const deployableScopes = parseList(getInput('deployable-scopes'))
106
- const allowMissingScopes = new Set(parseList(getInput('allow-missing-scopes')))
107
- const githubToken = getInput('github-token', { required: true })
108
-
109
- // Optional inputs - defaults from environment for portability
110
- const repositoryOwner = getInput('repository-owner', { defaultValue: process.env.GITHUB_REPOSITORY_OWNER })
111
- const repositoryPrefixInput = getInput('repository-prefix')
112
- const repositoryPrefix =
113
- repositoryPrefixInput || process.env.GHCR_REPOSITORY_PREFIX || process.env.GITHUB_REPOSITORY?.split('/')[1] || 'platform'
114
-
115
- // ============================================================================
116
- // Scope Discovery
117
- // ============================================================================
118
-
119
- const loadRoots = () => (scopeRoots.length > 0 ? scopeRoots : defaultRoots)
120
-
121
- /**
122
- * Resolves the scope name from package.json or falls back to directory name.
123
- * @param {string} entryPath - Path to the scope directory
124
- * @param {string} fallback - Fallback name (usually directory basename)
125
- * @returns {string} The resolved scope name
126
- */
127
- const resolveScopeName = (entryPath, fallback) => {
128
- const pkgPath = join(entryPath, 'package.json')
129
-
130
- if (!existsSync(pkgPath)) return fallback
131
-
132
- try {
133
- const raw = readFileSync(pkgPath, 'utf8')
134
- const pkg = JSON.parse(raw)
135
- if (typeof pkg.name === 'string' && pkg.name.trim().length > 0) {
136
- return pkg.name
137
- }
138
- } catch (error) {
139
- console.error(`Failed to read package.json for ${entryPath}:`, error)
140
- process.exit(1)
141
- }
142
-
143
- return fallback
144
- }
145
-
146
- const processScopeEntry = (root, entry, scopes) => {
147
- const entryPath = join(root, entry)
148
-
149
- const isDirectory = statSync(entryPath).isDirectory()
150
- if (!isDirectory) return
151
-
152
- const dockerfilePath = join(entryPath, 'Dockerfile')
153
- const hasDockerfile = existsSync(dockerfilePath)
154
- if (!hasDockerfile) return
155
-
156
- const scopeName = resolveScopeName(entryPath, entry)
157
- if (scopes.has(scopeName)) return
158
-
159
- const relativeDir = relative(process.cwd(), entryPath).replace(/\\/g, '/')
160
- scopes.set(scopeName, {
161
- name: scopeName,
162
- dir: relativeDir,
163
- shortName: basename(relativeDir),
164
- })
165
- }
166
-
167
- const discoverScopes = (roots) => {
168
- const scopes = new Map()
169
-
170
- for (const root of roots) {
171
- let entries = []
172
- try {
173
- entries = readdirSync(root)
174
- } catch (error) {
175
- if (error.code === 'ENOENT') {
176
- continue
177
- }
178
- throw error
179
- }
180
-
181
- for (const entry of entries) {
182
- processScopeEntry(root, entry, scopes)
183
- }
184
- }
185
-
186
- return scopes
187
- }
188
-
189
- const loadMetadata = () => {
190
- const metadataByScope = new Map()
191
- if (!existsSync(markersDir)) {
192
- return metadataByScope
193
- }
194
-
195
- const markers = readdirSync(markersDir).filter((name) => name.startsWith(changedMarkerPrefix))
196
-
197
- for (const marker of markers) {
198
- const markerPath = join(markersDir, marker)
199
- if (!existsSync(markerPath)) continue
200
-
201
- if (!lstatSync(markerPath).isDirectory()) continue
202
- const metadataPath = join(markerPath, 'metadata.json')
203
- if (!existsSync(metadataPath) || !lstatSync(metadataPath).isFile()) continue
204
-
205
- try {
206
- const raw = readFileSync(metadataPath, 'utf8')
207
- const parsed = JSON.parse(raw)
208
- if (
209
- parsed &&
210
- typeof parsed.shortName === 'string' &&
211
- parsed.shortName.trim() &&
212
- typeof parsed.imageTag === 'string' &&
213
- parsed.imageTag.trim()
214
- ) {
215
- metadataByScope.set(parsed.shortName.trim(), parsed.imageTag.trim())
216
- }
217
- } catch (error) {
218
- console.warn(`Unable to parse metadata at ${metadataPath}:`, error)
219
- }
220
- }
221
-
222
- return metadataByScope
223
- }
224
-
225
- const headers = {
226
- Authorization: `Bearer ${githubToken}`,
227
- Accept: 'application/vnd.github+json',
228
- 'X-GitHub-Api-Version': '2022-11-28',
229
- }
230
-
231
- const repositoryOwnerTypeInput = getInput('repository-owner-type').toLowerCase()
232
- const ownerTypeCandidates = (() => {
233
- if (repositoryOwnerTypeInput === 'user' || repositoryOwnerTypeInput === 'users') {
234
- return ['users']
235
- }
236
-
237
- if (
238
- repositoryOwnerTypeInput === 'org' ||
239
- repositoryOwnerTypeInput === 'orgs' ||
240
- repositoryOwnerTypeInput === 'organization'
241
- ) {
242
- return ['orgs']
243
- }
244
-
245
- return ['orgs', 'users']
246
- })()
247
-
248
- const requestPackageVersions = async (encodedPackage, page, ownerType) => {
249
- const ownerSegment = ownerType === 'users' ? 'users' : 'orgs'
250
- const url = `https://api.github.com/${ownerSegment}/${repositoryOwner}/packages/container/${encodedPackage}/versions?per_page=100&page=${page}`
251
- const response = await fetch(url, { headers })
252
-
253
- if (response.status === 404) {
254
- return { notFound: true, versions: [], hasMore: false }
255
- }
256
-
257
- if (!response.ok) {
258
- const text = await response.text()
259
- throw new Error(`GitHub API error (${response.status}): ${text}`)
260
- }
261
-
262
- const data = await response.json()
263
- const versions = Array.isArray(data) ? data : []
264
-
265
- return {
266
- versions,
267
- hasMore: versions.length === 100,
268
- notFound: false,
269
- }
270
- }
271
-
272
- const pickChecksumTag = (versions) => {
273
- for (const version of versions) {
274
- const tags = version?.metadata?.container?.tags
275
- if (!Array.isArray(tags)) continue
276
-
277
- // Prefer checksum tag (64 hex chars), fallback to 'latest'
278
- const checksumTag = tags.find((tag) => typeof tag === 'string' && /^[0-9a-f]{64}$/.test(tag))
279
- if (checksumTag) {
280
- return checksumTag
281
- }
282
- }
283
-
284
- // If no checksum tag found, look for 'latest' tag
285
- for (const version of versions) {
286
- const tags = version?.metadata?.container?.tags
287
- if (!Array.isArray(tags)) continue
288
-
289
- if (tags.includes('latest')) {
290
- return 'latest'
291
- }
292
- }
293
-
294
- return null
295
- }
296
-
297
- const fetchLatestChecksumTag = async (packageName) => {
298
- const encodedPackage = encodeURIComponent(packageName)
299
-
300
- for (const ownerType of ownerTypeCandidates) {
301
- let ownerMissing = false
302
-
303
- for (let page = 1; page <= maxPages; page += 1) {
304
- const { versions, hasMore, notFound } = await requestPackageVersions(encodedPackage, page, ownerType)
305
- if (notFound) {
306
- ownerMissing = true
307
- break
308
- }
309
-
310
- const checksumTag = pickChecksumTag(versions)
311
- if (checksumTag) {
312
- return checksumTag
313
- }
314
-
315
- if (!hasMore) {
316
- break
317
- }
318
- }
319
-
320
- if (ownerMissing) {
321
- console.warn(
322
- `Container package ${packageName} not found under ${ownerType === 'users' ? 'user' : 'organization'} ${repositoryOwner}. Trying alternate scope.`,
323
- )
324
- continue
325
- }
326
-
327
- break
328
- }
329
-
330
- return null
331
- }
332
-
333
- const main = async () => {
334
- const roots = loadRoots()
335
- const discoveredScopes = Array.from(discoverScopes(roots).values())
336
- const scopes = (() => {
337
- if (deployableScopes.length === 0) {
338
- return discoveredScopes
339
- }
340
-
341
- const scopeMap = new Map(discoveredScopes.map((scope) => [scope.shortName, scope]))
342
- const selected = []
343
-
344
- for (const name of deployableScopes) {
345
- const match = scopeMap.get(name)
346
- if (match) {
347
- selected.push(match)
348
- } else {
349
- console.warn(`DEPLOYABLE_SCOPES entry "${name}" was not found among discovered scopes`)
350
- }
351
- }
352
-
353
- return selected
354
- })()
355
-
356
- const metadataByScope = loadMetadata()
357
-
358
- if (scopes.length === 0) {
359
- console.warn('No scopes discovered; emitting empty scope_image_tags output')
360
- setOutput('scope_image_tags', '{}')
361
- return
362
- }
363
-
364
- const result = {}
365
- const missing = []
366
-
367
- // Get registry URL from environment (e.g., "ghcr.io/orderboss/platform")
368
- const registry = process.env.REGISTRY || `ghcr.io/${repositoryOwner}/${repositoryPrefix}`
369
-
370
- for (const scope of scopes) {
371
- const metadataTag = metadataByScope.get(scope.shortName)
372
- const imageTag = metadataTag || (await fetchLatestChecksumTag(`${repositoryPrefix}/${scope.shortName}`))
373
-
374
- if (!imageTag) {
375
- if (allowMissingScopes.has(scope.shortName)) {
376
- console.warn(`Skipping unresolved scope ${scope.shortName} (allowed missing)`)
377
- continue
378
- }
379
- missing.push(scope.shortName)
380
- continue
381
- }
382
-
383
- // Build full image URL: registry/service:tag
384
- result[scope.shortName] = `${registry}/${scope.shortName}:${imageTag}`
385
- }
386
-
387
- if (missing.length > 0) {
388
- console.error('Unable to resolve image tags for scopes:', missing.join(', '))
389
- process.exit(1)
390
- }
391
-
392
- setOutput('scope_image_tags', JSON.stringify(result))
393
- }
394
-
395
- main().catch((error) => {
396
- console.error('resolve-scope-tags action failed:', error)
397
- process.exit(1)
398
- })
@@ -1,57 +0,0 @@
1
- name: Setup Bun and install dependencies
2
- description: Installs Bun, optionally restores cache, and runs Bun install commands.
3
- author: {{githubOwner}}
4
-
5
- inputs:
6
- working-directory:
7
- description: Directory to run the install command in.
8
- default: .
9
- install-command:
10
- description: Command to install dependencies.
11
- default: bun install --frozen-lockfile
12
- enable-cache:
13
- description: Whether to run the cache step.
14
- default: 'false'
15
- cache-key:
16
- description: Cache key to use when caching is enabled.
17
- default: ''
18
- cache-restore-keys:
19
- description: Restore keys for the cache step.
20
- default: ''
21
- cache-paths:
22
- description: Newline-delimited paths to include in the cache.
23
- default: |
24
- ~/.bun
25
- node_modules
26
- bun-version-file:
27
- description: Path to a file containing the Bun version (passed to setup-bun).
28
- default: ''
29
- npm-token:
30
- description: NPM token for private registry access.
31
- required: false
32
- default: ''
33
-
34
- runs:
35
- using: composite
36
- steps:
37
- - name: Install Bun
38
- uses: oven-sh/setup-bun@v2
39
- with:
40
- bun-version-file: $\{{ inputs.bun-version-file }}
41
-
42
- - name: Cache dependencies
43
- if: $\{{ inputs.enable-cache == 'true' && inputs.cache-key != '' }}
44
- uses: actions/cache@v4
45
- with:
46
- path: $\{{ inputs.cache-paths }}
47
- key: $\{{ inputs.cache-key }}
48
- restore-keys: $\{{ inputs.cache-restore-keys }}
49
-
50
- - name: Install dependencies
51
- shell: bash
52
- env:
53
- NPM_TOKEN: $\{{ inputs.npm-token }}
54
- run: |
55
- set -euo pipefail
56
- cd "$\{{ inputs.working-directory }}"
57
- $\{{ inputs.install-command }}
@@ -1,49 +0,0 @@
1
- {
2
- "version": 1,
3
- "scope": {
4
- "include": [
5
- "apps",
6
- "services",
7
- "packages",
8
- "infra"
9
- ],
10
- "exclude": [
11
- "node_modules",
12
- "dist",
13
- "build",
14
- "coverage",
15
- ".turbo",
16
- ".github"
17
- ]
18
- },
19
- "guidelines": [
20
- "This repository is a Bun-based monorepo orchestrated with Turborepo. Prefer using existing scripts from package.json instead of introducing custom build steps.",
21
- "For backend services, use Hono or NestJS depending on the service. Do not introduce new server frameworks such as Express or Fastify.",
22
- "Payload validation must be implemented using Zod. Types should be inferred from Zod schemas (z.infer) rather than manually defined or using any.",
23
- "Infrastructure is defined via Pulumi inside the infra/ folder. New services must follow existing ServiceConfig patterns inside infra/services/*.ts.",
24
- "Code style follows the Biome configuration (biome.json): single quotes, no semicolons, 2-space indentation, and the configured max line width.",
25
- "Commit messages must follow Conventional Commits (feat:, fix:, chore:, docs:) to support release automation.",
26
- "Before introducing new patterns, inspect existing examples in the same folder or similar services/packages and follow them closely.",
27
- "Code should be elegant, readable, and intentionally simple. Prefer clarity over clever constructs.",
28
- "Comments, inline documentation, and JSDoc must be written in English.",
29
- "Avoid overly complex or deeply nested functions. Keep functions small, focused, and with a single responsibility.",
30
- "Avoid unnecessary higher-order abstractions or generic-heavy over-engineering. Only introduce abstractions when they simplify the implementation."
31
- ],
32
- "quickPrompts": [
33
- {
34
- "name": "Scaffold New Hono Microservice",
35
- "description": "Create a new Hono-based microservice using the platform-sdk.",
36
- "prompt": "Use the platform-sdk to scaffold a new Hono microservice. Run `bun pf new hono-micro services/<service-name>` and ensure the generated structure follows the existing pattern: telemetry as the first import, a health endpoint, proper port resolution from environment variables, and a matching infra config under infra/services/<service-name>.ts."
37
- },
38
- {
39
- "name": "Scaffold New NestJS Microservice",
40
- "description": "Generate a NestJS microservice using platform-sdk conventions.",
41
- "prompt": "Use `bun pf new nest-micro services/<service-name>` to generate a new NestJS microservice. Follow the established folder structure, ensure telemetry is loaded first, and apply the project's conventions. Rely on the generated template instead of inventing a custom layout."
42
- },
43
- {
44
- "name": "Create Service Infra Config",
45
- "description": "Generate a Pulumi ServiceConfig using existing infra patterns.",
46
- "prompt": "Create a new ServiceConfig file inside infra/services based on existing configurations. Include name, containerPort, healthCheck, env variables, and required secrets. Keep the configuration clean and document special aspects in English."
47
- }
48
- ]
49
- }
@@ -1,72 +0,0 @@
1
- # Copilot Instructions (Project-Agnostic, TypeScript-Focused)
2
-
3
- You are assisting in TypeScript-first monorepos using modern TypeScript tooling such as Bun or Node, Turborepo, Hono, NestJS, Zod, event-driven patterns, and Pulumi/Kubernetes infrastructure.
4
-
5
- Always generate **minimal diffs**, never full rewrites.
6
-
7
- ## General Behavior
8
- - Produce short, focused, code-first answers.
9
- - Modify only the necessary parts.
10
- - Analyze only the opened file unless explicitly asked.
11
- - Follow existing architecture and naming conventions.
12
- - Prefer strict typing; avoid `any`.
13
- - **Reuse existing code**: Before implementing new functionality, search the codebase for existing functions, utilities, or services that can be reused. Avoid duplicating logic.
14
-
15
- ## Code Style
16
- - Single quotes, no semicolons, 2-space indent, trailing commas.
17
- - Alphabetically sorted imports; no unused imports.
18
- - Arrow functions over `function` declarations.
19
- - Template literals over string concatenation.
20
- - Prefer pure and functional programming patterns (map/filter/reduce).
21
- - Avoid mutable state and imperative loops.
22
- - No decorative section header blocks (no ASCII art separators like `// ─────────────`).
23
- - Use blank lines to separate logical sections naturally.
24
- - Organize code: types → helpers → higher-order functions.
25
- - Use JSDoc for exported functions and complex logic; keep inline comments minimal.
26
- - Self-documenting code over comments: clear naming, pure functions, obvious control flow.
27
-
28
- ## Validation & Types
29
- - Use Zod for schemas.
30
- - Export inferred types using `z.infer`.
31
- - Do NOT include literal event types inside schemas.
32
- - Do not duplicate validation logic across layers.
33
-
34
- ## Service & Module Structure
35
- - Entry points handle wiring (server start, telemetry, routing, consumers).
36
- - Business logic lives in use-case modules.
37
- - Event handlers must be thin wrappers around use-cases.
38
-
39
- ## Event Handling
40
- - Use shared CloudEvents/messaging libraries, not raw clients.
41
- - Handlers: Schema → inferred type → thin handler → use-case delegation.
42
- - Handlers named `*.event.ts`.
43
-
44
- ## Infrastructure
45
- - Use fluent port builders when available.
46
- - Expose a `/health` endpoint.
47
- - Avoid legacy containerPort usage.
48
-
49
- ## Testing
50
- - Test use-cases, not handlers or frameworks.
51
- - Prefer simple direct tests, no mocks.
52
- - Validate both error and success paths.
53
-
54
- ## AI Output Format
55
- - Provide only necessary files.
56
- - Use relative paths.
57
- - Keep comments minimal.
58
- - Do not generate abstractions not already present.
59
-
60
- ## Service-Specific Guidelines
61
-
62
- When working with specific service types or packages, refer to these detailed guidelines:
63
-
64
- ### Service Generation & Architecture
65
- - [CLI Service Generator](../packages/platform-sdk/docs/generators/service.md) - AI code generation rules
66
-
67
- ### Key Packages
68
- - [@crossdelta/cloudevents](../packages/cloudevents/README.md) - Event handling with NATS and CloudEvents
69
- - [@crossdelta/telemetry](../packages/telemetry/README.md) - OpenTelemetry instrumentation
70
- - [@crossdelta/infrastructure](../packages/infrastructure/README.md) - Pulumi/K8s configuration
71
-
72
- **When generating services**: Always check the appropriate guidelines above to ensure correct patterns, especially for event-driven architectures.
@@ -1,18 +0,0 @@
1
- version: 2
2
- registries:
3
- npm-github:
4
- type: npm-registry
5
- url: https://npm.pkg.github.com
6
- token: ${{secrets.NPM_TOKEN}}
7
- replaces-base: true
8
-
9
- updates:
10
- - package-ecosystem: "bun"
11
- directory: "/"
12
- schedule:
13
- interval: "daily"
14
- open-pull-requests-limit: 10
15
- labels:
16
- - "dependencies"
17
- registries:
18
- - npm-github