@crossdelta/platform-sdk 0.21.29 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/bin/cli.mjs +100 -100
  3. package/bin/docs/generators/README.md +1 -1
  4. package/bin/templates/workspace/.claude/rules/cloudevents.md +85 -0
  5. package/bin/templates/workspace/.github/README.md +67 -0
  6. package/bin/templates/workspace/.github/actions/check-image-tag-exists/action.yml +27 -0
  7. package/bin/templates/workspace/.github/actions/check-image-tag-exists/index.js +179 -0
  8. package/bin/templates/workspace/.github/actions/generate-scope-matrix/action.yml +21 -0
  9. package/bin/templates/workspace/.github/actions/generate-scope-matrix/index.js +370 -0
  10. package/bin/templates/workspace/.github/actions/prepare-build-context/action.yml +167 -0
  11. package/bin/templates/workspace/.github/actions/setup-bun-install/action.yml.hbs +57 -0
  12. package/bin/templates/workspace/.github/dependabot.yml +18 -0
  13. package/bin/templates/workspace/.github/workflows/build-and-deploy.yml.hbs +409 -0
  14. package/bin/templates/workspace/.github/workflows/lint-and-tests.yml.hbs +83 -0
  15. package/bin/templates/workspace/.github/workflows/publish-packages.yml +228 -0
  16. package/bin/templates/workspace/.vscode/extensions.json +8 -0
  17. package/bin/templates/workspace/.vscode/settings.json +20 -0
  18. package/bin/templates/workspace/CLAUDE.md +76 -0
  19. package/bin/templates/workspace/apps/.gitkeep +0 -0
  20. package/bin/templates/workspace/docs/.gitkeep +0 -0
  21. package/bin/templates/workspace/infra/services/.gitkeep +0 -0
  22. package/bin/templates/workspace/packages/.gitkeep +0 -0
  23. package/bin/templates/workspace/packages/contracts/package.json.hbs +0 -1
  24. package/bin/templates/workspace/services/.gitkeep +0 -0
  25. package/package.json +1 -1
@@ -0,0 +1,228 @@
1
+ name: 📦 Publish Packages
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - 'packages/*/**'
9
+ - 'bun.lock'
10
+ - 'bunfig.toml'
11
+ workflow_dispatch:
12
+ inputs:
13
+ package:
14
+ description: 'Package to publish (slug from packages/ directory, or "all")'
15
+ type: string
16
+ default: all
17
+ required: true
18
+
19
+ permissions:
20
+ contents: read
21
+ id-token: write
22
+
23
+ jobs:
24
+ determine-packages:
25
+ runs-on: ubuntu-latest
26
+ outputs:
27
+ packages: ${{ steps.set-matrix.outputs.packages }}
28
+ count: ${{ steps.set-matrix.outputs.count }}
29
+ steps:
30
+ - name: Checkout repository
31
+ uses: actions/checkout@v6
32
+ with:
33
+ fetch-depth: 0
34
+
35
+ - name: Detect changed packages
36
+ id: changed
37
+ uses: tj-actions/changed-files@v47
38
+ with:
39
+ json: true
40
+ safe_output: false
41
+ files: packages/*/**
42
+
43
+ - name: Build package matrix
44
+ id: set-matrix
45
+ env:
46
+ INPUT_PACKAGE: ${{ github.event.inputs.package || '' }}
47
+ CHANGED_FILES: ${{ steps.changed.outputs.all_changed_files }}
48
+ run: |
49
+ declare -a packages=()
50
+ declare -A changed_packages
51
+
52
+ # Compare semver: true if $1 > $2
53
+ version_gt() {
54
+ [ "$(printf '%s\n' "$1" "$2" | sort -V | tail -1)" = "$1" ] && [ "$1" != "$2" ]
55
+ }
56
+
57
+ # Extract package names from changed file paths (e.g. packages/platform-sdk/src/foo.ts → platform-sdk)
58
+ if [ "${{ github.event_name }}" == "push" ] && [ -n "$CHANGED_FILES" ]; then
59
+ CLEAN_FILES=$(echo "$CHANGED_FILES" | sed 's/\\"/"/g')
60
+ for file in $(echo "$CLEAN_FILES" | jq -r '.[]' 2>/dev/null); do
61
+ if [[ $file =~ ^packages/([^/]+)/ ]]; then
62
+ changed_packages["${BASH_REMATCH[1]}"]=1
63
+ fi
64
+ done
65
+ fi
66
+
67
+ # Iterate all packages
68
+ for dir in packages/*/; do
69
+ [ ! -f "$dir/package.json" ] && continue
70
+
71
+ slug=$(basename "$dir")
72
+ dir="${dir%/}" # Remove trailing slash
73
+ name=$(jq -r '.name // empty' "$dir/package.json")
74
+ is_private=$(jq -r '.private // false' "$dir/package.json")
75
+
76
+ [ "$is_private" = "true" ] && continue
77
+ [ -z "$name" ] && continue
78
+
79
+ # Include package?
80
+ should_include=false
81
+ if [ -n "$INPUT_PACKAGE" ]; then
82
+ [ "$INPUT_PACKAGE" = "all" ] || [ "$INPUT_PACKAGE" = "$slug" ] && should_include=true
83
+ else
84
+ [ -n "${changed_packages[$slug]}" ] && should_include=true
85
+ fi
86
+
87
+ [ "$should_include" = true ] || continue
88
+
89
+ # Skip if local version <= npm version
90
+ local_ver=$(jq -r '.version' "$dir/package.json")
91
+ npm_ver=$(npm view "$name" version 2>/dev/null || echo "0.0.0")
92
+ if ! version_gt "$local_ver" "$npm_ver"; then
93
+ echo "⏭️ $name@$local_ver — npm has $npm_ver (same or newer) — skipping"
94
+ continue
95
+ fi
96
+
97
+ echo "📦 $name@$local_ver (npm: $npm_ver) — will publish"
98
+ packages+=("{\"name\":\"$name\",\"dir\":\"$dir\"}")
99
+ done
100
+
101
+ # Output
102
+ if [ ${#packages[@]} -eq 0 ]; then
103
+ echo "packages=[]" >> "$GITHUB_OUTPUT"
104
+ echo "count=0" >> "$GITHUB_OUTPUT"
105
+ else
106
+ echo "packages=[$(IFS=,; echo "${packages[*]}")]" >> "$GITHUB_OUTPUT"
107
+ echo "count=${#packages[@]}" >> "$GITHUB_OUTPUT"
108
+ echo "📦 Packages to publish: ${#packages[@]}"
109
+ fi
110
+
111
+ # Build all packages in dependency order using Turborepo
112
+ # This ensures infrastructure is built AFTER cloudevents (which it depends on)
113
+ build-all:
114
+ name: Build all packages
115
+ needs: determine-packages
116
+ if: needs.determine-packages.outputs.count != '0'
117
+ runs-on: ubuntu-latest
118
+ steps:
119
+ - uses: actions/checkout@v6
120
+
121
+ - name: Setup Bun and install dependencies
122
+ uses: ./.github/actions/setup-bun-install
123
+ with:
124
+ enable-cache: 'true'
125
+ cache-key: bun-v4-${{ runner.os }}-${{ hashFiles('bun.lock', 'bunfig.toml') }}
126
+ cache-restore-keys: bun-v4-${{ runner.os }}-
127
+
128
+ - name: Build all packages
129
+ run: bunx turbo run build --filter='./packages/*'
130
+
131
+ - name: Upload build artifacts
132
+ uses: actions/upload-artifact@v6
133
+ with:
134
+ name: dist-packages
135
+ # Required: the action drops every path beginning with a dot by default, and the
136
+ # workspace template the SDK ships is largely dotted (.claude/, .github/, .vscode/,
137
+ # .gitkeep). Without this the published package carries a template that `pf new
138
+ # workspace` cannot scaffold from, and nothing in the build or publish log says so.
139
+ include-hidden-files: true
140
+ path: |
141
+ packages/*/dist
142
+ packages/*/bin
143
+ packages/*/*.md
144
+ packages/*/LICENSE
145
+ packages/*/*.png
146
+ packages/*/schemas
147
+ packages/*/*.sh
148
+ retention-days: 1
149
+
150
+ # Publish packages (parallel, no git push needed)
151
+ # Only packages with local version > npm reach this job
152
+ # Uses npm Trusted Publishing (OIDC) — no token required
153
+ publish:
154
+ name: Publish ${{ matrix.package.name }}
155
+ needs: [determine-packages, build-all]
156
+ if: needs.determine-packages.outputs.count != '0'
157
+ runs-on: ubuntu-latest
158
+ permissions:
159
+ contents: read
160
+ id-token: write
161
+ strategy:
162
+ matrix:
163
+ package: ${{ fromJson(needs.determine-packages.outputs.packages) }}
164
+ steps:
165
+ - uses: actions/checkout@v6
166
+
167
+ - name: Setup Node.js
168
+ uses: actions/setup-node@v5
169
+ with:
170
+ node-version: 24
171
+ registry-url: https://registry.npmjs.org
172
+
173
+ - name: Download build artifacts
174
+ uses: actions/download-artifact@v7
175
+ with:
176
+ name: dist-packages
177
+ path: packages
178
+
179
+ - name: Verify build artifacts
180
+ run: |
181
+ echo "📂 Checking ${{ matrix.package.dir }}"
182
+ ls -la "${{ matrix.package.dir }}" || true
183
+ if [ ! -d "${{ matrix.package.dir }}/dist" ] && [ ! -d "${{ matrix.package.dir }}/bin" ]; then
184
+ echo "❌ Build artifacts missing for ${{ matrix.package.name }}"
185
+ echo "📂 packages/ contents:"
186
+ ls -la packages/
187
+ exit 1
188
+ fi
189
+ echo "✅ Build artifacts found for ${{ matrix.package.name }}"
190
+
191
+ # A present bin/ is not a complete bin/. Artifact upload silently drops dotted paths unless
192
+ # include-hidden-files is set, which shipped a workspace template without .github/ for months
193
+ # while every build and publish log stayed green.
194
+ - name: Verify the workspace template survived the artifact round-trip
195
+ run: |
196
+ template="${{ matrix.package.dir }}/bin/templates/workspace"
197
+ if [ ! -d "$template" ]; then
198
+ echo "ℹ️ ${{ matrix.package.name }} ships no workspace template, nothing to verify"
199
+ exit 0
200
+ fi
201
+ missing=0
202
+ for required in .claude/rules .github/workflows .vscode; do
203
+ if [ ! -d "$template/$required" ]; then
204
+ echo "❌ $template/$required is missing from the artifact"
205
+ missing=1
206
+ fi
207
+ done
208
+ if [ "$missing" = "1" ]; then
209
+ echo "Hidden paths were dropped. Check include-hidden-files on the upload step."
210
+ exit 1
211
+ fi
212
+ echo "✅ Workspace template complete"
213
+
214
+ - name: Publish to npm
215
+ working-directory: ${{ matrix.package.dir }}
216
+ run: |
217
+ # Resolve workspace:* to real versions from sibling packages
218
+ for pkg in ../*/package.json; do
219
+ n=$(jq -r .name "$pkg"); v=$(jq -r .version "$pkg")
220
+ tmp=$(mktemp)
221
+ jq --arg n "$n" --arg v "^$v" '
222
+ (if .dependencies[$n] then .dependencies[$n] = $v else . end) |
223
+ (if .peerDependencies[$n] then .peerDependencies[$n] = $v else . end)
224
+ ' package.json > "$tmp" && mv "$tmp" package.json
225
+ done
226
+ VERSION=$(jq -r '.version' package.json)
227
+ echo "📦 Publishing ${{ matrix.package.name }}@$VERSION"
228
+ npm publish --access=public --ignore-scripts
@@ -0,0 +1,8 @@
1
+ {
2
+ "recommendations": [
3
+ "biomejs.biome",
4
+ "github.copilot",
5
+ "github.copilot-chat",
6
+ "pulumi.pulumi-lsp-client"
7
+ ]
8
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "editor.defaultFormatter": "biomejs.biome",
3
+ "editor.formatOnSave": true,
4
+ "editor.codeActionsOnSave": {
5
+ "source.fixAll.biome": "explicit",
6
+ "source.organizeImports.biome": "explicit"
7
+ },
8
+ "[typescript]": {
9
+ "editor.defaultFormatter": "biomejs.biome"
10
+ },
11
+ "[typescriptreact]": {
12
+ "editor.defaultFormatter": "biomejs.biome"
13
+ },
14
+ "[javascript]": {
15
+ "editor.defaultFormatter": "biomejs.biome"
16
+ },
17
+ "[json]": {
18
+ "editor.defaultFormatter": "biomejs.biome"
19
+ }
20
+ }
@@ -0,0 +1,76 @@
1
+ # Project 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
+ ## Core Principles
6
+
7
+ - Functional style: pure functions, composition over inheritance, side effects isolated in adapters.
8
+ - Separation of concerns: validation → logic → I/O. Domain logic separate from transport.
9
+ - Minimal diffs: change only what's needed, no full rewrites, no extra abstractions.
10
+ - Configuration over constants: avoid hard-coded values.
11
+
12
+ ## Safety Rules
13
+
14
+ - **NEVER** echo secrets/credentials into terminal output: they become conversation context sent to the AI provider.
15
+ - Use `source .env.local 2>/dev/null`. Never inline secrets as command arguments.
16
+ - **Never commit or push without explicit user confirmation.**
17
+
18
+ ## Before Writing Code
19
+
20
+ - **Search first**: Check the codebase for existing utilities before implementing new ones. Reuse > reinvent.
21
+ - **Check installed versions**: Verify the actual API of installed packages (`package.json`, `node_modules/`), don't assume.
22
+ - **Check generators**: Look for `gen`/`generate`/`codegen` scripts in `package.json` before editing files that might be auto-generated.
23
+
24
+ ## After Writing Code
25
+
26
+ - **Test consumers**: When changing a shared package (`packages/*`), run tests in **all consuming services**, not just the package itself. Changes to shared code have blast radius.
27
+ - **Push before publish**: Always push commits to remote before triggering npm publish workflows. The CI reads from the repo, not your local machine.
28
+ - **Pre-commit hooks exist**: The repo has a `sync-templates` hook that auto-updates downstream `package.json` refs after version bumps. Never bypass with `--no-verify`.
29
+ - **Capture lessons**: When a convention or pitfall emerges, propose updating a scoped `.claude/rules/` file. Ask before creating new files. Keep `CLAUDE.md` project-agnostic; domain-specific rules go in scoped files with `paths:`.
30
+
31
+ ## Monorepo Awareness
32
+
33
+ - Internal dependencies use `workspace:*` protocol. Never pin specific versions for workspace packages.
34
+ - Docker builds use `prepare-build-context` which flattens the monorepo, so root `package.json` `overrides` are **lost** in the Docker build. If a transitive dependency needs pinning, the fix must be in the package's own `package.json`.
35
+ - Turborepo caches builds. When debugging stale output, try `turbo run build --force`.
36
+
37
+ ## Scoped Rules
38
+
39
+ This workspace comes with one rule:
40
+
41
+ - [CloudEvents & Event Handling](.claude/rules/cloudevents.md): handler pattern, wildcard handlers, JetStream vs Core, contracts, NATS migrations (`services/*/src/events/**`, `packages/contracts/**`)
42
+
43
+ Add your own as they earn their place. A rule is worth writing once a convention has cost you
44
+ something: a pitfall someone hit, a boundary someone crossed. Rules written ahead of that describe
45
+ a codebase that does not exist yet, and get ignored.
46
+
47
+ To add one: create `.claude/rules/<name>.md`, give it a `paths:` list of globs the repo actually
48
+ has, and keep it to what the code cannot say about itself. A rule with `paths:` loads when a
49
+ matching file is touched. Leave `paths:` out only for something that binds in every session
50
+ regardless of which file is open, because that cost is paid on every turn.
51
+
52
+ Code style and testing conventions are not rules here: the `pf` generators carry their own, and they
53
+ load whenever the generator writes code.
54
+
55
+ ## Key Packages
56
+
57
+ Installed from npm, not vendored into this repo. Read their APIs in `node_modules/`, not from memory.
58
+
59
+ - `@crossdelta/cloudevents` - Event handling with NATS and CloudEvents
60
+ - `@crossdelta/telemetry` - OpenTelemetry instrumentation
61
+ - `@crossdelta/infrastructure` - Pulumi/K8s configuration
62
+
63
+ ## AI Output Format
64
+
65
+ - Provide only necessary files.
66
+ - Use relative paths.
67
+ - Keep comments minimal.
68
+ - Do not generate abstractions not already present.
69
+
70
+ ## Writing Style for Documentation
71
+
72
+ When writing to files (README, docs, plan documents, comments):
73
+
74
+ - No em dashes (`—`). Use a colon or restructure the sentence.
75
+ - No AI-speak: avoid "ships with", "wired up", "out of the box", "seamlessly", "ready to use", "powerful", "robust".
76
+ - Plain sentences. If a phrase sounds like marketing copy, rewrite it as a statement of fact.
File without changes
File without changes
File without changes
@@ -21,7 +21,6 @@
21
21
  "dependencies": {
22
22
  "@crossdelta/cloudevents": "^0.8.4",
23
23
  "@crossdelta/infrastructure": "^0.15.2",
24
- "{{scope}}/types": "workspace:*",
25
24
  "zod": "^4.0.0"
26
25
  },
27
26
  "devDependencies": {
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/platform-sdk",
3
- "version": "0.21.29",
3
+ "version": "0.22.1",
4
4
  "description": "Platform toolkit for event-driven microservices — keeping code and infrastructure in lockstep.",
5
5
  "keywords": [
6
6
  "cli",