diff --git a/.claude/skills/pnpm-postinstall-deps/SKILL.md b/.claude/skills/pnpm-postinstall-deps/SKILL.md
new file mode 100644
index 000000000..7227b44d7
--- /dev/null
+++ b/.claude/skills/pnpm-postinstall-deps/SKILL.md
@@ -0,0 +1,61 @@
+---
+name: pnpm-postinstall-deps
+description: Use when user adds package with postinstall script (supabase, sharp), gets "binary not found" errors in CI, or asks about onlyBuiltDependencies. Explains the two-step pattern for managing packages that need build/postinstall scripts in pnpm workspaces with frozen lockfiles.
+---
+
+# pnpm Postinstall Dependencies
+
+Manage packages that need postinstall scripts in pnpm workspaces.
+
+## The Problem
+
+`pnpm install --frozen-lockfile --prefer-offline` in CI restores from cache and skips postinstall scripts. Packages like `supabase` download binaries via postinstall - without it, the binary doesn't exist.
+
+## The Pattern
+
+**Two-step sync required:**
+
+1. **pnpm-workspace.yaml** - Declares which packages are allowed to run build scripts
+2. **Setup action** - Explicitly rebuilds those packages after install
+
+
+Both lists MUST stay in sync. If they diverge:
+- Missing in yaml → Package can't run postinstall (security)
+- Missing in action → Binary won't be available in CI
+
+
+## Checklist
+
+When adding a package that needs postinstall:
+
+### 1. Add to pnpm-workspace.yaml
+
+```yaml
+onlyBuiltDependencies:
+ - supabase
+ - sharp
+ - your-new-package # Add here
+```
+
+### 2. Update .github/actions/setup/action.yml
+
+```yaml
+- name: Rebuild packages that need postinstall (onlyBuiltDependencies)
+ shell: bash
+ run: pnpm rebuild supabase sharp your-new-package # Add here
+```
+
+### 3. Verify
+
+Check both files have identical package lists.
+
+## Why Explicit Package Names?
+
+`pnpm rebuild --pending` is too implicit - unclear if it respects onlyBuiltDependencies. Explicit names guarantee behavior and make configuration visible.
+
+## Common Packages
+
+- `supabase` - Downloads CLI binary from GitHub releases
+- `sharp` - Builds native image processing library
+- `esbuild` - Downloads platform-specific binary
+- `puppeteer` - Downloads Chromium binary
diff --git a/apps/demo/cloudflare-worker-plausible-proxy.js b/apps/demo/cloudflare-worker-plausible-proxy.js
index bda89bde8..9aeca83c4 100644
--- a/apps/demo/cloudflare-worker-plausible-proxy.js
+++ b/apps/demo/cloudflare-worker-plausible-proxy.js
@@ -1,15 +1,9 @@
/**
* Cloudflare Worker for Plausible Analytics Proxy
*
- * IMPORTANT: You MUST update the ProxyScript variable below with your
- * actual Plausible script URL from your dashboard before deploying!
+ * This worker proxies Plausible Analytics scripts and events to avoid ad-blocker detection
*/
-// CONFIGURATION - UPDATE THIS VALUE!
-// Get this from your Plausible dashboard (Site Settings > Site Installation)
-// It will look like: https://plausible.io/js/script.js (or pa-XXXXX.js for custom domains)
-const ProxyScript = 'https://plausible.io/js/script.js'; // ← UPDATE THIS!
-
// Customize these paths to avoid ad-blocker detection
const ScriptName = '/stats/script.js';
const Endpoint = '/stats/event';
@@ -18,34 +12,39 @@ const Endpoint = '/stats/event';
const ScriptWithoutExtension = ScriptName.replace('.js', '');
addEventListener('fetch', (event) => {
- event.passThroughOnException();
- event.respondWith(handleRequest(event));
+ event.passThroughOnException();
+ event.respondWith(handleRequest(event));
});
async function handleRequest(event) {
- const pathname = new URL(event.request.url).pathname;
- const [baseUri] = pathname.split('.');
+ const pathname = new URL(event.request.url).pathname;
+ const [baseUri, ...extensions] = pathname.split('.');
- if (baseUri.endsWith(ScriptWithoutExtension)) {
- return getScript(event);
- } else if (pathname.endsWith(Endpoint)) {
- return postData(event);
- }
+ if (baseUri.endsWith(ScriptWithoutExtension)) {
+ return getScript(event, extensions);
+ } else if (pathname.endsWith(Endpoint)) {
+ return postData(event);
+ }
- return new Response(null, { status: 404 });
+ return new Response(null, { status: 404 });
}
-async function getScript(event) {
- let response = await caches.default.match(event.request);
- if (!response) {
- response = await fetch(ProxyScript);
- event.waitUntil(caches.default.put(event.request, response.clone()));
- }
- return response;
+async function getScript(event, extensions) {
+ let response = await caches.default.match(event.request);
+ if (!response) {
+ // Fetch the standard Plausible script with any extensions
+ // Default to 'plausible.js' if no extensions are provided
+ const scriptPath = extensions.length > 0
+ ? 'plausible.' + extensions.join('.')
+ : 'plausible.js';
+ response = await fetch('https://plausible.io/js/' + scriptPath);
+ event.waitUntil(caches.default.put(event.request, response.clone()));
+ }
+ return response;
}
async function postData(event) {
- const request = new Request(event.request);
- request.headers.delete('cookie');
- return await fetch('https://plausible.io/api/event', request);
-}
+ const request = new Request(event.request);
+ request.headers.delete('cookie');
+ return await fetch('https://plausible.io/api/event', request);
+}
\ No newline at end of file
diff --git a/apps/demo/src/lib/components/ExplanationPanel.svelte b/apps/demo/src/lib/components/ExplanationPanel.svelte
index 6a4984e22..c68747614 100644
--- a/apps/demo/src/lib/components/ExplanationPanel.svelte
+++ b/apps/demo/src/lib/components/ExplanationPanel.svelte
@@ -50,17 +50,17 @@
explanation:
'Unique identifier for this flow. Must be ≤128 characters, containing only letters, numbers, and underscores.'
},
- {
- name: 'maxAttempts',
- value: '2',
- explanation:
- 'Maximum total attempts allowed. With 2 attempts: 1 initial try + 1 retry on failure.'
- },
{
name: 'baseDelay',
value: '1',
explanation:
'Base delay for retries in seconds. With exponential backoff, first retry waits 1s (subsequent retries would wait 2s, 4s, etc).'
+ },
+ {
+ name: 'maxAttempts',
+ value: '2',
+ explanation:
+ 'Maximum total attempts allowed. With 2 attempts: 1 initial try + 1 retry on failure.'
}
],
inputType: `{
diff --git a/apps/demo/src/lib/data/flow-code.ts b/apps/demo/src/lib/data/flow-code.ts
index 22b3c12fe..2e0d04137 100644
--- a/apps/demo/src/lib/data/flow-code.ts
+++ b/apps/demo/src/lib/data/flow-code.ts
@@ -19,7 +19,7 @@ export const FLOW_SECTIONS: Record = {
flow_config: {
code: `new Flow<{ url: string }>({
slug: 'article_flow',
- baseDelay:1,
+ baseDelay: 1,
maxAttempts: 2
})`
},
diff --git a/apps/demo/src/routes/+layout.svelte b/apps/demo/src/routes/+layout.svelte
index 3fd4182a3..319107a2f 100644
--- a/apps/demo/src/routes/+layout.svelte
+++ b/apps/demo/src/routes/+layout.svelte
@@ -10,8 +10,8 @@
diff --git a/apps/demo/src/routes/+page.svelte b/apps/demo/src/routes/+page.svelte
index fc3973cf2..289350756 100644
--- a/apps/demo/src/routes/+page.svelte
+++ b/apps/demo/src/routes/+page.svelte
@@ -571,6 +571,8 @@
track('Logo Clicked', { location: 'header' })}
>
@@ -580,6 +582,8 @@
|
track('External Link Clicked', { destination: 'website', location: 'header' })}
@@ -588,6 +592,8 @@
|
track('External Link Clicked', { destination: 'github', location: 'header' })}
diff --git a/apps/demo/tsconfig.json b/apps/demo/tsconfig.json
index 3840a59d6..0dc4a6071 100644
--- a/apps/demo/tsconfig.json
+++ b/apps/demo/tsconfig.json
@@ -9,7 +9,8 @@
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
- "moduleResolution": "bundler"
+ "moduleResolution": "bundler",
+ "composite": true
},
"references": [
{
diff --git a/pkgs/client/vite.config.ts b/pkgs/client/vite.config.ts
index 22b7eb088..6bb155d28 100644
--- a/pkgs/client/vite.config.ts
+++ b/pkgs/client/vite.config.ts
@@ -7,16 +7,17 @@ export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/pkgs/client',
plugins: [
- dts({
+ dts({
include: ['src/**/*.ts'],
outDir: 'dist',
rollupTypes: false, // Don't bundle for now
insertTypesEntry: true,
- tsConfigFilePath: resolve(__dirname, 'tsconfig.lib.json'),
- skipDiagnostics: true // Skip TypeScript diagnostics to avoid vite-plugin-dts errors with monorepo project references.
- // The plugin tries to compile all imported files (including from other packages)
+ tsconfigPath: resolve(__dirname, 'tsconfig.lib.json'),
+ skipDiagnostics: true // Skip TypeScript diagnostics to avoid vite-plugin-dts errors with monorepo project references.
+ // The plugin tries to compile all imported files (including from other packages)
// which breaks rootDir boundaries. Nx runs proper type checking separately.
- })
+ }) as any // Type cast to avoid Vite version mismatch between packages
+
],
build: {
lib: {
diff --git a/pkgs/dsl/__tests__/types/array-method.test-d.ts b/pkgs/dsl/__tests__/types/array-method.test-d.ts
index 0d7fa7d74..8086cdaf3 100644
--- a/pkgs/dsl/__tests__/types/array-method.test-d.ts
+++ b/pkgs/dsl/__tests__/types/array-method.test-d.ts
@@ -178,7 +178,7 @@ describe('.array() method type constraints', () => {
// ExtractFlowContext should include FlowContext & custom resources
type FlowCtx = ExtractFlowContext;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
+
expectTypeOf().toMatchTypeOf<{
env: Record;
shutdownSignal: AbortSignal;
diff --git a/pkgs/website/src/env.d.ts b/pkgs/website/src/env.d.ts
index 5d34fb160..5a39abfe9 100644
--- a/pkgs/website/src/env.d.ts
+++ b/pkgs/website/src/env.d.ts
@@ -1,6 +1,6 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
///
-// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+
///
interface ImportMetaEnv {
diff --git a/tsconfig.json b/tsconfig.json
index 1015cbfe8..3729a7d1e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -20,6 +20,9 @@
},
{
"path": "./pkgs/client"
+ },
+ {
+ "path": "./apps/demo"
}
]
}