@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.
- package/CHANGELOG.md +31 -0
- package/bin/cli.mjs +100 -100
- package/bin/docs/generators/README.md +1 -1
- package/bin/templates/workspace/.claude/rules/cloudevents.md +85 -0
- package/bin/templates/workspace/.github/README.md +67 -0
- package/bin/templates/workspace/.github/actions/check-image-tag-exists/action.yml +27 -0
- package/bin/templates/workspace/.github/actions/check-image-tag-exists/index.js +179 -0
- package/bin/templates/workspace/.github/actions/generate-scope-matrix/action.yml +21 -0
- package/bin/templates/workspace/.github/actions/generate-scope-matrix/index.js +370 -0
- package/bin/templates/workspace/.github/actions/prepare-build-context/action.yml +167 -0
- package/bin/templates/workspace/.github/actions/setup-bun-install/action.yml.hbs +57 -0
- package/bin/templates/workspace/.github/dependabot.yml +18 -0
- package/bin/templates/workspace/.github/workflows/build-and-deploy.yml.hbs +409 -0
- package/bin/templates/workspace/.github/workflows/lint-and-tests.yml.hbs +83 -0
- package/bin/templates/workspace/.github/workflows/publish-packages.yml +228 -0
- package/bin/templates/workspace/.vscode/extensions.json +8 -0
- package/bin/templates/workspace/.vscode/settings.json +20 -0
- package/bin/templates/workspace/CLAUDE.md +76 -0
- package/bin/templates/workspace/apps/.gitkeep +0 -0
- package/bin/templates/workspace/docs/.gitkeep +0 -0
- package/bin/templates/workspace/infra/services/.gitkeep +0 -0
- package/bin/templates/workspace/packages/.gitkeep +0 -0
- package/bin/templates/workspace/packages/contracts/package.json.hbs +0 -1
- package/bin/templates/workspace/services/.gitkeep +0 -0
- 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,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
|
|
File without changes
|
|
File without changes
|