runwork 0.15.0 → 0.15.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.
- package/dist/index.js +19272 -116
- package/package.json +4 -4
- package/dist/__tests__/curl-parser.test.d.ts +0 -1
- package/dist/__tests__/curl-parser.test.js +0 -250
- package/dist/__tests__/install-scripts.test.d.ts +0 -1
- package/dist/__tests__/install-scripts.test.js +0 -280
- package/dist/__tests__/types-manager.test.d.ts +0 -1
- package/dist/__tests__/types-manager.test.js +0 -89
- package/dist/agents/__tests__/chatgpt-registry.test.d.ts +0 -1
- package/dist/agents/__tests__/chatgpt-registry.test.js +0 -61
- package/dist/agents/__tests__/claude-code-managed-block.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-managed-block.test.js +0 -97
- package/dist/agents/__tests__/claude-code-plugin.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-plugin.test.js +0 -47
- package/dist/agents/__tests__/claude-code-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-stats.test.js +0 -390
- package/dist/agents/__tests__/claude-desktop-memory.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-memory.test.js +0 -154
- package/dist/agents/__tests__/claude-desktop-plugin-tree.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-plugin-tree.test.js +0 -161
- package/dist/agents/__tests__/claude-desktop-plugin.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-plugin.test.js +0 -117
- package/dist/agents/__tests__/claude-desktop-rpm.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-rpm.test.js +0 -126
- package/dist/agents/__tests__/claude-desktop-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-stats.test.js +0 -280
- package/dist/agents/__tests__/codex-instructions.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-instructions.test.js +0 -57
- package/dist/agents/__tests__/codex-minimum-permissions.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-minimum-permissions.test.js +0 -82
- package/dist/agents/__tests__/codex-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-stats.test.js +0 -370
- package/dist/agents/__tests__/cursor-merge.test.d.ts +0 -1
- package/dist/agents/__tests__/cursor-merge.test.js +0 -66
- package/dist/agents/__tests__/cursor-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/cursor-stats.test.js +0 -183
- package/dist/agents/__tests__/defaults-merge.test.d.ts +0 -1
- package/dist/agents/__tests__/defaults-merge.test.js +0 -268
- package/dist/agents/__tests__/detection.test.d.ts +0 -1
- package/dist/agents/__tests__/detection.test.js +0 -89
- package/dist/agents/__tests__/graceful-degradation.test.d.ts +0 -1
- package/dist/agents/__tests__/graceful-degradation.test.js +0 -129
- package/dist/agents/__tests__/instruction-hint.test.d.ts +0 -1
- package/dist/agents/__tests__/instruction-hint.test.js +0 -69
- package/dist/agents/__tests__/intro-skill.test.d.ts +0 -1
- package/dist/agents/__tests__/intro-skill.test.js +0 -193
- package/dist/agents/__tests__/schema-validation.test.d.ts +0 -1
- package/dist/agents/__tests__/schema-validation.test.js +0 -212
- package/dist/agents/__tests__/skill-slug.test.d.ts +0 -1
- package/dist/agents/__tests__/skill-slug.test.js +0 -77
- package/dist/agents/claude-code.d.ts +0 -53
- package/dist/agents/claude-code.js +0 -754
- package/dist/agents/claude-desktop-plugin-tree.d.ts +0 -59
- package/dist/agents/claude-desktop-plugin-tree.js +0 -143
- package/dist/agents/claude-desktop.d.ts +0 -47
- package/dist/agents/claude-desktop.js +0 -684
- package/dist/agents/cline.d.ts +0 -15
- package/dist/agents/cline.js +0 -130
- package/dist/agents/codex.d.ts +0 -44
- package/dist/agents/codex.js +0 -453
- package/dist/agents/conversation-skills.d.ts +0 -18
- package/dist/agents/conversation-skills.js +0 -229
- package/dist/agents/cursor.d.ts +0 -48
- package/dist/agents/cursor.js +0 -324
- package/dist/agents/default-config.d.ts +0 -30
- package/dist/agents/default-config.js +0 -67
- package/dist/agents/defaults-merge.d.ts +0 -72
- package/dist/agents/defaults-merge.js +0 -131
- package/dist/agents/detect.d.ts +0 -4
- package/dist/agents/detect.js +0 -67
- package/dist/agents/detection.d.ts +0 -18
- package/dist/agents/detection.js +0 -94
- package/dist/agents/gemini.d.ts +0 -15
- package/dist/agents/gemini.js +0 -122
- package/dist/agents/generic-adapter.d.ts +0 -25
- package/dist/agents/generic-adapter.js +0 -151
- package/dist/agents/intro-skill.d.ts +0 -45
- package/dist/agents/intro-skill.js +0 -447
- package/dist/agents/registry-data.d.ts +0 -192
- package/dist/agents/registry-data.js +0 -669
- package/dist/agents/registry.d.ts +0 -13
- package/dist/agents/registry.js +0 -36
- package/dist/agents/runtime-detection.d.ts +0 -82
- package/dist/agents/runtime-detection.js +0 -271
- package/dist/agents/session-start-hook.d.ts +0 -37
- package/dist/agents/session-start-hook.js +0 -159
- package/dist/agents/types.d.ts +0 -171
- package/dist/agents/types.js +0 -75
- package/dist/agents/utils/instruction-hint.d.ts +0 -21
- package/dist/agents/utils/instruction-hint.js +0 -108
- package/dist/agents/utils/json-config.d.ts +0 -19
- package/dist/agents/utils/json-config.js +0 -69
- package/dist/agents/utils/toml-config.d.ts +0 -8
- package/dist/agents/utils/toml-config.js +0 -27
- package/dist/agents/windsurf.d.ts +0 -14
- package/dist/agents/windsurf.js +0 -102
- package/dist/api/__tests__/client.test.d.ts +0 -1
- package/dist/api/__tests__/client.test.js +0 -272
- package/dist/api/client.d.ts +0 -326
- package/dist/api/client.js +0 -427
- package/dist/auth/__tests__/login-flow.test.d.ts +0 -1
- package/dist/auth/__tests__/login-flow.test.js +0 -101
- package/dist/auth/__tests__/store.test.d.ts +0 -1
- package/dist/auth/__tests__/store.test.js +0 -132
- package/dist/auth/login-flow.d.ts +0 -26
- package/dist/auth/login-flow.js +0 -137
- package/dist/auth/store.d.ts +0 -5
- package/dist/auth/store.js +0 -62
- package/dist/commands/__tests__/clone-args.test.d.ts +0 -1
- package/dist/commands/__tests__/clone-args.test.js +0 -51
- package/dist/commands/__tests__/clone-source-result.test.d.ts +0 -1
- package/dist/commands/__tests__/clone-source-result.test.js +0 -45
- package/dist/commands/__tests__/info-merge.test.d.ts +0 -1
- package/dist/commands/__tests__/info-merge.test.js +0 -55
- package/dist/commands/__tests__/mcp-entries.test.d.ts +0 -1
- package/dist/commands/__tests__/mcp-entries.test.js +0 -48
- package/dist/commands/__tests__/setup-persona.test.d.ts +0 -1
- package/dist/commands/__tests__/setup-persona.test.js +0 -31
- package/dist/commands/__tests__/setup.test.d.ts +0 -1
- package/dist/commands/__tests__/setup.test.js +0 -81
- package/dist/commands/__tests__/sync-redetect.test.d.ts +0 -1
- package/dist/commands/__tests__/sync-redetect.test.js +0 -96
- package/dist/commands/__tests__/sync-telemetry.test.d.ts +0 -1
- package/dist/commands/__tests__/sync-telemetry.test.js +0 -281
- package/dist/commands/__tests__/upgrade.test.d.ts +0 -1
- package/dist/commands/__tests__/upgrade.test.js +0 -157
- package/dist/commands/agents.d.ts +0 -2
- package/dist/commands/agents.js +0 -160
- package/dist/commands/api-keys.d.ts +0 -2
- package/dist/commands/api-keys.js +0 -95
- package/dist/commands/apps.d.ts +0 -2
- package/dist/commands/apps.js +0 -82
- package/dist/commands/build-plugin.d.ts +0 -2
- package/dist/commands/build-plugin.js +0 -135
- package/dist/commands/clone.d.ts +0 -60
- package/dist/commands/clone.js +0 -271
- package/dist/commands/components.d.ts +0 -2
- package/dist/commands/components.js +0 -43
- package/dist/commands/deploy.d.ts +0 -2
- package/dist/commands/deploy.js +0 -279
- package/dist/commands/dev.d.ts +0 -11
- package/dist/commands/dev.js +0 -1011
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.js +0 -230
- package/dist/commands/endpoints.d.ts +0 -2
- package/dist/commands/endpoints.js +0 -189
- package/dist/commands/entities.d.ts +0 -2
- package/dist/commands/entities.js +0 -198
- package/dist/commands/files.d.ts +0 -2
- package/dist/commands/files.js +0 -193
- package/dist/commands/inbox.d.ts +0 -11
- package/dist/commands/inbox.js +0 -60
- package/dist/commands/info.d.ts +0 -178
- package/dist/commands/info.js +0 -455
- package/dist/commands/init.d.ts +0 -21
- package/dist/commands/init.js +0 -185
- package/dist/commands/integrations.d.ts +0 -2
- package/dist/commands/integrations.js +0 -233
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +0 -44
- package/dist/commands/logout.d.ts +0 -2
- package/dist/commands/logout.js +0 -13
- package/dist/commands/logs.d.ts +0 -2
- package/dist/commands/logs.js +0 -232
- package/dist/commands/mcp-entries.d.ts +0 -13
- package/dist/commands/mcp-entries.js +0 -26
- package/dist/commands/mcp.d.ts +0 -2
- package/dist/commands/mcp.js +0 -245
- package/dist/commands/open.d.ts +0 -2
- package/dist/commands/open.js +0 -53
- package/dist/commands/resume.d.ts +0 -2
- package/dist/commands/resume.js +0 -289
- package/dist/commands/save-convo.d.ts +0 -8
- package/dist/commands/save-convo.js +0 -20
- package/dist/commands/schedules.d.ts +0 -2
- package/dist/commands/schedules.js +0 -221
- package/dist/commands/setup.d.ts +0 -27
- package/dist/commands/setup.js +0 -177
- package/dist/commands/share-convo.d.ts +0 -24
- package/dist/commands/share-convo.js +0 -167
- package/dist/commands/skills.d.ts +0 -2
- package/dist/commands/skills.js +0 -352
- package/dist/commands/sync-telemetry.d.ts +0 -33
- package/dist/commands/sync-telemetry.js +0 -186
- package/dist/commands/sync.d.ts +0 -37
- package/dist/commands/sync.js +0 -702
- package/dist/commands/uninstall.d.ts +0 -2
- package/dist/commands/uninstall.js +0 -145
- package/dist/commands/upgrade.d.ts +0 -14
- package/dist/commands/upgrade.js +0 -217
- package/dist/commands/validate.d.ts +0 -2
- package/dist/commands/validate.js +0 -89
- package/dist/commands/welcome.d.ts +0 -1
- package/dist/commands/welcome.js +0 -86
- package/dist/commands/workflows.d.ts +0 -2
- package/dist/commands/workflows.js +0 -187
- package/dist/deploy/__tests__/deploy-state.test.d.ts +0 -1
- package/dist/deploy/__tests__/deploy-state.test.js +0 -80
- package/dist/deploy/__tests__/deploy-status.test.d.ts +0 -1
- package/dist/deploy/__tests__/deploy-status.test.js +0 -41
- package/dist/deploy/__tests__/detach-args.test.d.ts +0 -1
- package/dist/deploy/__tests__/detach-args.test.js +0 -30
- package/dist/deploy/deploy-state.d.ts +0 -36
- package/dist/deploy/deploy-state.js +0 -63
- package/dist/deploy/deploy-status.d.ts +0 -21
- package/dist/deploy/deploy-status.js +0 -36
- package/dist/deploy/detach.d.ts +0 -32
- package/dist/deploy/detach.js +0 -71
- package/dist/dev/__tests__/attach.test.d.ts +0 -1
- package/dist/dev/__tests__/attach.test.js +0 -296
- package/dist/dev/__tests__/detach.test.d.ts +0 -1
- package/dist/dev/__tests__/detach.test.js +0 -404
- package/dist/dev/__tests__/preview-url-poller.test.d.ts +0 -1
- package/dist/dev/__tests__/preview-url-poller.test.js +0 -149
- package/dist/dev/__tests__/session.test.d.ts +0 -1
- package/dist/dev/__tests__/session.test.js +0 -446
- package/dist/dev/__tests__/startup-sync.test.d.ts +0 -1
- package/dist/dev/__tests__/startup-sync.test.js +0 -32
- package/dist/dev/__tests__/stop.test.d.ts +0 -1
- package/dist/dev/__tests__/stop.test.js +0 -172
- package/dist/dev/attach.d.ts +0 -120
- package/dist/dev/attach.js +0 -269
- package/dist/dev/detach.d.ts +0 -187
- package/dist/dev/detach.js +0 -292
- package/dist/dev/preview-url-poller.d.ts +0 -35
- package/dist/dev/preview-url-poller.js +0 -50
- package/dist/dev/session.d.ts +0 -175
- package/dist/dev/session.js +0 -258
- package/dist/dev/startup-sync.d.ts +0 -23
- package/dist/dev/startup-sync.js +0 -14
- package/dist/dev/stop.d.ts +0 -52
- package/dist/dev/stop.js +0 -101
- package/dist/devtools/registry-data.d.ts +0 -46
- package/dist/devtools/registry-data.js +0 -40
- package/dist/generated/bundled-types.d.ts +0 -1
- package/dist/generated/bundled-types.js +0 -46
- package/dist/generated/version.d.ts +0 -1
- package/dist/generated/version.js +0 -2
- package/dist/git/__tests__/auto-commit.test.d.ts +0 -1
- package/dist/git/__tests__/auto-commit.test.js +0 -431
- package/dist/git/__tests__/classify-sync-error.test.d.ts +0 -1
- package/dist/git/__tests__/classify-sync-error.test.js +0 -57
- package/dist/git/__tests__/credential-helper-e2e.test.d.ts +0 -21
- package/dist/git/__tests__/credential-helper-e2e.test.js +0 -195
- package/dist/git/__tests__/credentials.test.d.ts +0 -1
- package/dist/git/__tests__/credentials.test.js +0 -141
- package/dist/git/__tests__/deploy-guard.test.d.ts +0 -1
- package/dist/git/__tests__/deploy-guard.test.js +0 -61
- package/dist/git/__tests__/ensure-git-credential-helper.test.d.ts +0 -1
- package/dist/git/__tests__/ensure-git-credential-helper.test.js +0 -133
- package/dist/git/__tests__/ensure-runwork-remote.test.d.ts +0 -1
- package/dist/git/__tests__/ensure-runwork-remote.test.js +0 -109
- package/dist/git/__tests__/identity.test.d.ts +0 -1
- package/dist/git/__tests__/identity.test.js +0 -146
- package/dist/git/__tests__/manifest.test.d.ts +0 -1
- package/dist/git/__tests__/manifest.test.js +0 -446
- package/dist/git/__tests__/preflight-resolution.test.d.ts +0 -1
- package/dist/git/__tests__/preflight-resolution.test.js +0 -366
- package/dist/git/__tests__/preflight.test.d.ts +0 -1
- package/dist/git/__tests__/preflight.test.js +0 -36
- package/dist/git/__tests__/repo-config.test.d.ts +0 -1
- package/dist/git/__tests__/repo-config.test.js +0 -26
- package/dist/git/__tests__/sync.test.d.ts +0 -1
- package/dist/git/__tests__/sync.test.js +0 -405
- package/dist/git/auto-commit.d.ts +0 -35
- package/dist/git/auto-commit.js +0 -293
- package/dist/git/classify-sync-error.d.ts +0 -21
- package/dist/git/classify-sync-error.js +0 -111
- package/dist/git/credentials.d.ts +0 -75
- package/dist/git/credentials.js +0 -164
- package/dist/git/critical-files.d.ts +0 -20
- package/dist/git/critical-files.js +0 -68
- package/dist/git/deploy-guard.d.ts +0 -53
- package/dist/git/deploy-guard.js +0 -78
- package/dist/git/identity.d.ts +0 -44
- package/dist/git/identity.js +0 -133
- package/dist/git/preflight.d.ts +0 -57
- package/dist/git/preflight.js +0 -276
- package/dist/git/remote.d.ts +0 -37
- package/dist/git/remote.js +0 -75
- package/dist/git/repo-config.d.ts +0 -8
- package/dist/git/repo-config.js +0 -29
- package/dist/git/sync.d.ts +0 -15
- package/dist/git/sync.js +0 -159
- package/dist/health/__tests__/checks.test.d.ts +0 -1
- package/dist/health/__tests__/checks.test.js +0 -357
- package/dist/health/__tests__/cli-distribution-checks.test.d.ts +0 -1
- package/dist/health/__tests__/cli-distribution-checks.test.js +0 -196
- package/dist/health/__tests__/fix.test.d.ts +0 -1
- package/dist/health/__tests__/fix.test.js +0 -72
- package/dist/health/__tests__/runner-filter.test.d.ts +0 -1
- package/dist/health/__tests__/runner-filter.test.js +0 -61
- package/dist/health/checks.d.ts +0 -42
- package/dist/health/checks.js +0 -636
- package/dist/health/fix.d.ts +0 -22
- package/dist/health/fix.js +0 -62
- package/dist/health/runner.d.ts +0 -8
- package/dist/health/runner.js +0 -47
- package/dist/health/types.d.ts +0 -12
- package/dist/health/types.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/logs/__tests__/tailer-format.test.d.ts +0 -1
- package/dist/logs/__tests__/tailer-format.test.js +0 -43
- package/dist/logs/tailer.d.ts +0 -17
- package/dist/logs/tailer.js +0 -217
- package/dist/sync/__tests__/change-detect.test.d.ts +0 -1
- package/dist/sync/__tests__/change-detect.test.js +0 -123
- package/dist/sync/__tests__/hash.test.d.ts +0 -1
- package/dist/sync/__tests__/hash.test.js +0 -18
- package/dist/sync/change-detect.d.ts +0 -19
- package/dist/sync/change-detect.js +0 -136
- package/dist/sync/conflict-ui.d.ts +0 -5
- package/dist/sync/conflict-ui.js +0 -78
- package/dist/sync/executor.d.ts +0 -23
- package/dist/sync/executor.js +0 -102
- package/dist/sync/hash.d.ts +0 -1
- package/dist/sync/hash.js +0 -4
- package/dist/sync/types.d.ts +0 -24
- package/dist/sync/types.js +0 -1
- package/dist/template/manifest.d.ts +0 -17
- package/dist/template/manifest.js +0 -145
- package/dist/tools/types.d.ts +0 -52
- package/dist/tools/types.js +0 -8
- package/dist/types-manager.d.ts +0 -1
- package/dist/types-manager.js +0 -35
- package/dist/types.d.ts +0 -349
- package/dist/types.js +0 -1
- package/dist/ui/__tests__/banner.test.d.ts +0 -1
- package/dist/ui/__tests__/banner.test.js +0 -98
- package/dist/ui/__tests__/colors.test.d.ts +0 -1
- package/dist/ui/__tests__/colors.test.js +0 -22
- package/dist/ui/__tests__/keyboard.test.d.ts +0 -1
- package/dist/ui/__tests__/keyboard.test.js +0 -34
- package/dist/ui/__tests__/status-line.test.d.ts +0 -1
- package/dist/ui/__tests__/status-line.test.js +0 -54
- package/dist/ui/banner.d.ts +0 -29
- package/dist/ui/banner.js +0 -153
- package/dist/ui/colors.d.ts +0 -4
- package/dist/ui/colors.js +0 -7
- package/dist/ui/keyboard.d.ts +0 -12
- package/dist/ui/keyboard.js +0 -61
- package/dist/ui/status-line.d.ts +0 -6
- package/dist/ui/status-line.js +0 -53
- package/dist/utils/__tests__/format-error.test.d.ts +0 -1
- package/dist/utils/__tests__/format-error.test.js +0 -43
- package/dist/utils/__tests__/help-json.test.d.ts +0 -1
- package/dist/utils/__tests__/help-json.test.js +0 -39
- package/dist/utils/__tests__/http.test.d.ts +0 -1
- package/dist/utils/__tests__/http.test.js +0 -381
- package/dist/utils/__tests__/ignore-matcher.test.d.ts +0 -1
- package/dist/utils/__tests__/ignore-matcher.test.js +0 -205
- package/dist/utils/__tests__/output.test.d.ts +0 -1
- package/dist/utils/__tests__/output.test.js +0 -38
- package/dist/utils/__tests__/prompt.test.d.ts +0 -1
- package/dist/utils/__tests__/prompt.test.js +0 -27
- package/dist/utils/__tests__/resolve.test.d.ts +0 -1
- package/dist/utils/__tests__/resolve.test.js +0 -95
- package/dist/utils/__tests__/sqlite.test.d.ts +0 -1
- package/dist/utils/__tests__/sqlite.test.js +0 -89
- package/dist/utils/agent-guidance.d.ts +0 -47
- package/dist/utils/agent-guidance.js +0 -128
- package/dist/utils/app-info.d.ts +0 -70
- package/dist/utils/app-info.js +0 -167
- package/dist/utils/curl-parser.d.ts +0 -12
- package/dist/utils/curl-parser.js +0 -162
- package/dist/utils/data-input.d.ts +0 -1
- package/dist/utils/data-input.js +0 -38
- package/dist/utils/format-error.d.ts +0 -10
- package/dist/utils/format-error.js +0 -38
- package/dist/utils/fs.d.ts +0 -5
- package/dist/utils/fs.js +0 -23
- package/dist/utils/help-json.d.ts +0 -70
- package/dist/utils/help-json.js +0 -88
- package/dist/utils/http.d.ts +0 -46
- package/dist/utils/http.js +0 -421
- package/dist/utils/ignore-matcher.d.ts +0 -47
- package/dist/utils/ignore-matcher.js +0 -132
- package/dist/utils/output.d.ts +0 -17
- package/dist/utils/output.js +0 -27
- package/dist/utils/prompt.d.ts +0 -5
- package/dist/utils/prompt.js +0 -42
- package/dist/utils/resolve.d.ts +0 -12
- package/dist/utils/resolve.js +0 -52
- package/dist/utils/setup-state.d.ts +0 -3
- package/dist/utils/setup-state.js +0 -19
- package/dist/utils/sqlite-adapter.d.ts +0 -48
- package/dist/utils/sqlite-adapter.js +0 -147
- package/dist/utils/sqlite.d.ts +0 -14
- package/dist/utils/sqlite.js +0 -14
- package/dist/utils/subprocess.d.ts +0 -19
- package/dist/utils/subprocess.js +0 -27
- package/dist/utils/which.d.ts +0 -14
- package/dist/utils/which.js +0 -47
- package/dist/utils/zip.d.ts +0 -7
- package/dist/utils/zip.js +0 -42
- package/dist/validate/__tests__/validate.test.d.ts +0 -1
- package/dist/validate/__tests__/validate.test.js +0 -42
- package/dist/validate/freshness.d.ts +0 -28
- package/dist/validate/freshness.js +0 -48
- package/dist/validate/preview.d.ts +0 -21
- package/dist/validate/preview.js +0 -39
- package/dist/workspace/resolve.d.ts +0 -19
- package/dist/workspace/resolve.js +0 -100
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
|
|
2
|
-
import { createServer } from 'node:http';
|
|
3
|
-
import { gzipSync, deflateSync, brotliCompressSync } from 'node:zlib';
|
|
4
|
-
import { spawnSync } from 'node:child_process';
|
|
5
|
-
import { httpFetch, __setTransportForTests } from '../http.js';
|
|
6
|
-
let server;
|
|
7
|
-
let port;
|
|
8
|
-
const calls = [];
|
|
9
|
-
function startServer() {
|
|
10
|
-
return new Promise((resolve) => {
|
|
11
|
-
server = createServer(async (req, res) => {
|
|
12
|
-
const chunks = [];
|
|
13
|
-
for await (const chunk of req)
|
|
14
|
-
chunks.push(chunk);
|
|
15
|
-
const body = Buffer.concat(chunks).toString('utf-8');
|
|
16
|
-
calls.push({ url: req.url || '', method: req.method || '', headers: req.headers, body });
|
|
17
|
-
const url = req.url || '/';
|
|
18
|
-
// Simple endpoints exercised by the test cases below.
|
|
19
|
-
if (url === '/json') {
|
|
20
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
21
|
-
res.end(JSON.stringify({ hello: 'world' }));
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (url === '/text') {
|
|
25
|
-
res.writeHead(200, { 'content-type': 'text/plain' });
|
|
26
|
-
res.end('plain body');
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (url === '/binary') {
|
|
30
|
-
res.writeHead(200, { 'content-type': 'application/octet-stream' });
|
|
31
|
-
res.end(Buffer.from([1, 2, 3, 4, 5]));
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (url === '/echo-headers') {
|
|
35
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
36
|
-
res.end(JSON.stringify({ headers: req.headers, method: req.method }));
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (url === '/echo-body') {
|
|
40
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
41
|
-
res.end(JSON.stringify({ body, contentType: req.headers['content-type'] }));
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
if (url === '/status/404') {
|
|
45
|
-
res.writeHead(404, { 'content-type': 'text/plain' });
|
|
46
|
-
res.end('not found');
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (url === '/status/500') {
|
|
50
|
-
res.writeHead(500, { 'content-type': 'text/plain' });
|
|
51
|
-
res.end('boom');
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (url === '/redirect/302') {
|
|
55
|
-
res.writeHead(302, { location: '/json' });
|
|
56
|
-
res.end();
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (url === '/redirect/303-post') {
|
|
60
|
-
res.writeHead(303, { location: '/echo-headers' });
|
|
61
|
-
res.end();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (url === '/redirect/307') {
|
|
65
|
-
res.writeHead(307, { location: '/echo-body' });
|
|
66
|
-
res.end();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (url === '/redirect/loop') {
|
|
70
|
-
res.writeHead(302, { location: '/redirect/loop' });
|
|
71
|
-
res.end();
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if (url === '/gzip') {
|
|
75
|
-
res.writeHead(200, { 'content-type': 'text/plain', 'content-encoding': 'gzip' });
|
|
76
|
-
res.end(gzipSync(Buffer.from('compressed-gzip-body')));
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (url === '/deflate') {
|
|
80
|
-
res.writeHead(200, { 'content-type': 'text/plain', 'content-encoding': 'deflate' });
|
|
81
|
-
res.end(deflateSync(Buffer.from('compressed-deflate-body')));
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (url === '/brotli') {
|
|
85
|
-
res.writeHead(200, { 'content-type': 'text/plain', 'content-encoding': 'br' });
|
|
86
|
-
res.end(brotliCompressSync(Buffer.from('compressed-brotli-body')));
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (url === '/head') {
|
|
90
|
-
res.writeHead(200, { 'content-type': 'text/plain', 'x-marker': 'present' });
|
|
91
|
-
res.end();
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
res.writeHead(404);
|
|
95
|
-
res.end();
|
|
96
|
-
});
|
|
97
|
-
server.listen(0, '127.0.0.1', () => {
|
|
98
|
-
const addr = server.address();
|
|
99
|
-
if (typeof addr === 'object' && addr && 'port' in addr) {
|
|
100
|
-
port = addr.port;
|
|
101
|
-
}
|
|
102
|
-
resolve();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
beforeAll(async () => { await startServer(); });
|
|
107
|
-
afterAll(() => new Promise((resolve) => server.close(() => resolve())));
|
|
108
|
-
function url(path) { return `http://127.0.0.1:${port}${path}`; }
|
|
109
|
-
describe('httpFetch — basic Response surface', () => {
|
|
110
|
-
it('parses JSON responses', async () => {
|
|
111
|
-
const res = await httpFetch(url('/json'));
|
|
112
|
-
expect(res.ok).toBe(true);
|
|
113
|
-
expect(res.status).toBe(200);
|
|
114
|
-
expect(res.headers.get('content-type')).toContain('application/json');
|
|
115
|
-
expect(await res.json()).toEqual({ hello: 'world' });
|
|
116
|
-
});
|
|
117
|
-
it('reads text responses', async () => {
|
|
118
|
-
const res = await httpFetch(url('/text'));
|
|
119
|
-
expect(res.ok).toBe(true);
|
|
120
|
-
expect(await res.text()).toBe('plain body');
|
|
121
|
-
});
|
|
122
|
-
it('reads binary responses as ArrayBuffer with the exact byte content', async () => {
|
|
123
|
-
const res = await httpFetch(url('/binary'));
|
|
124
|
-
const ab = await res.arrayBuffer();
|
|
125
|
-
expect(new Uint8Array(ab)).toEqual(new Uint8Array([1, 2, 3, 4, 5]));
|
|
126
|
-
});
|
|
127
|
-
it('returns ok=false for 4xx without throwing', async () => {
|
|
128
|
-
const res = await httpFetch(url('/status/404'));
|
|
129
|
-
expect(res.ok).toBe(false);
|
|
130
|
-
expect(res.status).toBe(404);
|
|
131
|
-
expect(await res.text()).toBe('not found');
|
|
132
|
-
});
|
|
133
|
-
it('returns ok=false for 5xx without throwing', async () => {
|
|
134
|
-
const res = await httpFetch(url('/status/500'));
|
|
135
|
-
expect(res.ok).toBe(false);
|
|
136
|
-
expect(res.status).toBe(500);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
describe('httpFetch — request shape', () => {
|
|
140
|
-
it('passes through plain object headers, lowercased per HTTP convention', async () => {
|
|
141
|
-
const res = await httpFetch(url('/echo-headers'), {
|
|
142
|
-
headers: { 'X-Custom-Foo': 'bar', Authorization: 'Bearer abc' },
|
|
143
|
-
});
|
|
144
|
-
const data = await res.json();
|
|
145
|
-
expect(data.method).toBe('GET');
|
|
146
|
-
expect(data.headers['x-custom-foo']).toBe('bar');
|
|
147
|
-
expect(data.headers['authorization']).toBe('Bearer abc');
|
|
148
|
-
});
|
|
149
|
-
it('passes through Headers instance', async () => {
|
|
150
|
-
const headers = new Headers();
|
|
151
|
-
headers.set('X-One', 'a');
|
|
152
|
-
headers.set('X-Two', 'b');
|
|
153
|
-
const res = await httpFetch(url('/echo-headers'), { headers });
|
|
154
|
-
const data = await res.json();
|
|
155
|
-
expect(data.headers['x-one']).toBe('a');
|
|
156
|
-
expect(data.headers['x-two']).toBe('b');
|
|
157
|
-
});
|
|
158
|
-
it('uppercases lowercase methods', async () => {
|
|
159
|
-
const res = await httpFetch(url('/echo-headers'), { method: 'post', body: '' });
|
|
160
|
-
const data = await res.json();
|
|
161
|
-
expect(data.method).toBe('POST');
|
|
162
|
-
});
|
|
163
|
-
it('sends string body and content-length', async () => {
|
|
164
|
-
const res = await httpFetch(url('/echo-body'), {
|
|
165
|
-
method: 'POST',
|
|
166
|
-
headers: { 'content-type': 'application/json' },
|
|
167
|
-
body: JSON.stringify({ a: 1 }),
|
|
168
|
-
});
|
|
169
|
-
const data = await res.json();
|
|
170
|
-
expect(data.body).toBe('{"a":1}');
|
|
171
|
-
expect(data.contentType).toBe('application/json');
|
|
172
|
-
});
|
|
173
|
-
it('sends Buffer body', async () => {
|
|
174
|
-
const res = await httpFetch(url('/echo-body'), {
|
|
175
|
-
method: 'PUT',
|
|
176
|
-
body: Buffer.from('raw-buffer'),
|
|
177
|
-
});
|
|
178
|
-
const data = await res.json();
|
|
179
|
-
expect(data.body).toBe('raw-buffer');
|
|
180
|
-
});
|
|
181
|
-
it('sends Uint8Array body with offset', async () => {
|
|
182
|
-
const backing = new Uint8Array([0, 0, 65, 66, 67, 0]);
|
|
183
|
-
const view = backing.subarray(2, 5); // "ABC"
|
|
184
|
-
const res = await httpFetch(url('/echo-body'), { method: 'POST', body: view });
|
|
185
|
-
const data = await res.json();
|
|
186
|
-
expect(data.body).toBe('ABC');
|
|
187
|
-
});
|
|
188
|
-
it('sends ArrayBuffer body', async () => {
|
|
189
|
-
const ab = new TextEncoder().encode('via-arraybuffer').buffer;
|
|
190
|
-
const res = await httpFetch(url('/echo-body'), { method: 'POST', body: ab });
|
|
191
|
-
const data = await res.json();
|
|
192
|
-
expect(data.body).toBe('via-arraybuffer');
|
|
193
|
-
});
|
|
194
|
-
it('drops body on bodyless GET/HEAD methods (matches fetch behavior)', async () => {
|
|
195
|
-
const res = await httpFetch(url('/echo-body'), {
|
|
196
|
-
method: 'GET',
|
|
197
|
-
// GET requests with bodies are non-conformant; fetch silently drops the body.
|
|
198
|
-
body: 'should-not-be-sent',
|
|
199
|
-
});
|
|
200
|
-
const data = await res.json();
|
|
201
|
-
expect(data.body).toBe('');
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
describe('httpFetch — redirects', () => {
|
|
205
|
-
it('follows a 302 to the final URL', async () => {
|
|
206
|
-
const res = await httpFetch(url('/redirect/302'));
|
|
207
|
-
expect(res.status).toBe(200);
|
|
208
|
-
expect(res.url.endsWith('/json')).toBe(true);
|
|
209
|
-
expect(await res.json()).toEqual({ hello: 'world' });
|
|
210
|
-
});
|
|
211
|
-
it('downgrades POST to GET on 303 (matches fetch semantics)', async () => {
|
|
212
|
-
const res = await httpFetch(url('/redirect/303-post'), {
|
|
213
|
-
method: 'POST',
|
|
214
|
-
body: 'ignored-after-303',
|
|
215
|
-
});
|
|
216
|
-
const data = await res.json();
|
|
217
|
-
expect(data.method).toBe('GET');
|
|
218
|
-
});
|
|
219
|
-
it('preserves method and body on 307', async () => {
|
|
220
|
-
const res = await httpFetch(url('/redirect/307'), {
|
|
221
|
-
method: 'POST',
|
|
222
|
-
body: 'preserved-on-307',
|
|
223
|
-
});
|
|
224
|
-
const data = await res.json();
|
|
225
|
-
expect(data.body).toBe('preserved-on-307');
|
|
226
|
-
});
|
|
227
|
-
it('rejects after exceeding the redirect limit', async () => {
|
|
228
|
-
// The endpoint redirects to itself; after MAX_REDIRECTS we stop following
|
|
229
|
-
// and return the redirect response itself rather than crash.
|
|
230
|
-
const res = await httpFetch(url('/redirect/loop'));
|
|
231
|
-
expect(res.status).toBe(302);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
describe('httpFetch — content decoding', () => {
|
|
235
|
-
it('advertises gzip/deflate/br by default', async () => {
|
|
236
|
-
await httpFetch(url('/echo-headers'));
|
|
237
|
-
const last = calls[calls.length - 1];
|
|
238
|
-
expect(last.headers['accept-encoding']).toBe('gzip, deflate, br');
|
|
239
|
-
});
|
|
240
|
-
it('decompresses gzip responses transparently', async () => {
|
|
241
|
-
const res = await httpFetch(url('/gzip'));
|
|
242
|
-
expect(await res.text()).toBe('compressed-gzip-body');
|
|
243
|
-
});
|
|
244
|
-
it('decompresses brotli responses transparently', async () => {
|
|
245
|
-
const res = await httpFetch(url('/brotli'));
|
|
246
|
-
expect(await res.text()).toBe('compressed-brotli-body');
|
|
247
|
-
});
|
|
248
|
-
it('decompresses deflate responses transparently', async () => {
|
|
249
|
-
const res = await httpFetch(url('/deflate'));
|
|
250
|
-
expect(await res.text()).toBe('compressed-deflate-body');
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
describe('httpFetch — HEAD requests', () => {
|
|
254
|
-
it('returns headers and an empty body', async () => {
|
|
255
|
-
const res = await httpFetch(url('/head'), { method: 'HEAD' });
|
|
256
|
-
expect(res.status).toBe(200);
|
|
257
|
-
expect(res.headers.get('x-marker')).toBe('present');
|
|
258
|
-
expect(await res.text()).toBe('');
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
describe('httpFetch — errors', () => {
|
|
262
|
-
it('rejects on connection failure', async () => {
|
|
263
|
-
// 127.0.0.1:1 is virtually guaranteed to refuse connections.
|
|
264
|
-
await expect(httpFetch('http://127.0.0.1:1/')).rejects.toThrow();
|
|
265
|
-
});
|
|
266
|
-
it('rejects on unsupported protocols', async () => {
|
|
267
|
-
await expect(httpFetch('ftp://example.com/')).rejects.toThrow(/Unsupported protocol/);
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
271
|
-
// curl-transport tests
|
|
272
|
-
//
|
|
273
|
-
// `curl` is the opt-in escape hatch (`RUNWORK_HTTP_TRANSPORT=curl`). It
|
|
274
|
-
// shells out to the system `curl` so HTTP runs in a subprocess, immune to
|
|
275
|
-
// any future runtime regression. These tests force the curl path via the
|
|
276
|
-
// test-only override so the break-glass path stays exercised in CI even
|
|
277
|
-
// though the default transport is `node:https`. Skipped when curl is
|
|
278
|
-
// missing on the host.
|
|
279
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
280
|
-
const curlAvailable = (() => {
|
|
281
|
-
try {
|
|
282
|
-
const r = spawnSync('curl', ['--version'], { encoding: 'utf-8' });
|
|
283
|
-
return r.status === 0;
|
|
284
|
-
}
|
|
285
|
-
catch {
|
|
286
|
-
return false;
|
|
287
|
-
}
|
|
288
|
-
})();
|
|
289
|
-
describe.skipIf(!curlAvailable)('httpFetch — curl transport (opt-in via env var)', () => {
|
|
290
|
-
beforeAll(() => { __setTransportForTests('curl'); });
|
|
291
|
-
afterAll(() => { __setTransportForTests('auto'); });
|
|
292
|
-
it('parses JSON responses through curl', async () => {
|
|
293
|
-
const res = await httpFetch(url('/json'));
|
|
294
|
-
expect(res.ok).toBe(true);
|
|
295
|
-
expect(res.status).toBe(200);
|
|
296
|
-
expect(await res.json()).toEqual({ hello: 'world' });
|
|
297
|
-
});
|
|
298
|
-
it('reads binary responses with exact byte content', async () => {
|
|
299
|
-
const res = await httpFetch(url('/binary'));
|
|
300
|
-
const ab = await res.arrayBuffer();
|
|
301
|
-
expect(new Uint8Array(ab)).toEqual(new Uint8Array([1, 2, 3, 4, 5]));
|
|
302
|
-
});
|
|
303
|
-
it('returns ok=false for 4xx without throwing', async () => {
|
|
304
|
-
const res = await httpFetch(url('/status/404'));
|
|
305
|
-
expect(res.ok).toBe(false);
|
|
306
|
-
expect(res.status).toBe(404);
|
|
307
|
-
expect(await res.text()).toBe('not found');
|
|
308
|
-
});
|
|
309
|
-
it('follows 302 redirects to the final URL', async () => {
|
|
310
|
-
const res = await httpFetch(url('/redirect/302'));
|
|
311
|
-
expect(res.status).toBe(200);
|
|
312
|
-
expect(res.url.endsWith('/json')).toBe(true);
|
|
313
|
-
expect(await res.json()).toEqual({ hello: 'world' });
|
|
314
|
-
});
|
|
315
|
-
it('downgrades POST to GET on 303 (matches fetch semantics)', async () => {
|
|
316
|
-
const res = await httpFetch(url('/redirect/303-post'), {
|
|
317
|
-
method: 'POST',
|
|
318
|
-
body: 'ignored-after-303',
|
|
319
|
-
});
|
|
320
|
-
const data = await res.json();
|
|
321
|
-
expect(data.method).toBe('GET');
|
|
322
|
-
});
|
|
323
|
-
it('preserves method and body on 307', async () => {
|
|
324
|
-
const res = await httpFetch(url('/redirect/307'), {
|
|
325
|
-
method: 'POST',
|
|
326
|
-
body: 'preserved-on-307',
|
|
327
|
-
});
|
|
328
|
-
const data = await res.json();
|
|
329
|
-
expect(data.body).toBe('preserved-on-307');
|
|
330
|
-
});
|
|
331
|
-
it('passes plain object headers through', async () => {
|
|
332
|
-
const res = await httpFetch(url('/echo-headers'), {
|
|
333
|
-
headers: { 'X-Custom': 'curl-roundtrip', Authorization: 'Bearer abc' },
|
|
334
|
-
});
|
|
335
|
-
const data = await res.json();
|
|
336
|
-
expect(data.headers['x-custom']).toBe('curl-roundtrip');
|
|
337
|
-
expect(data.headers['authorization']).toBe('Bearer abc');
|
|
338
|
-
});
|
|
339
|
-
it('sends string body and content-type', async () => {
|
|
340
|
-
const res = await httpFetch(url('/echo-body'), {
|
|
341
|
-
method: 'POST',
|
|
342
|
-
headers: { 'content-type': 'application/json' },
|
|
343
|
-
body: JSON.stringify({ a: 1 }),
|
|
344
|
-
});
|
|
345
|
-
const data = await res.json();
|
|
346
|
-
expect(data.body).toBe('{"a":1}');
|
|
347
|
-
expect(data.contentType).toBe('application/json');
|
|
348
|
-
});
|
|
349
|
-
it('sends binary Uint8Array body with offset', async () => {
|
|
350
|
-
const backing = new Uint8Array([0, 0, 65, 66, 67, 0]);
|
|
351
|
-
const view = backing.subarray(2, 5); // "ABC"
|
|
352
|
-
const res = await httpFetch(url('/echo-body'), { method: 'POST', body: view });
|
|
353
|
-
const data = await res.json();
|
|
354
|
-
expect(data.body).toBe('ABC');
|
|
355
|
-
});
|
|
356
|
-
it('returns headers and an empty body for HEAD', async () => {
|
|
357
|
-
const res = await httpFetch(url('/head'), { method: 'HEAD' });
|
|
358
|
-
expect(res.status).toBe(200);
|
|
359
|
-
expect(res.headers.get('x-marker')).toBe('present');
|
|
360
|
-
expect(await res.text()).toBe('');
|
|
361
|
-
});
|
|
362
|
-
it('warns and returns raw bytes on unexpected gzip', async () => {
|
|
363
|
-
const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
364
|
-
try {
|
|
365
|
-
const res = await httpFetch(url('/gzip'));
|
|
366
|
-
expect(res.ok).toBe(true);
|
|
367
|
-
const ab = await res.arrayBuffer();
|
|
368
|
-
expect(ab.byteLength).toBeGreaterThan(0);
|
|
369
|
-
expect(stderr).toHaveBeenCalledWith(expect.stringMatching(/Content-Encoding: gzip/));
|
|
370
|
-
}
|
|
371
|
-
finally {
|
|
372
|
-
stderr.mockRestore();
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
it('rejects on connection failure', async () => {
|
|
376
|
-
await expect(httpFetch('http://127.0.0.1:1/')).rejects.toThrow();
|
|
377
|
-
});
|
|
378
|
-
it('rejects on unsupported protocols', async () => {
|
|
379
|
-
await expect(httpFetch('ftp://example.com/')).rejects.toThrow(/Unsupported protocol/);
|
|
380
|
-
});
|
|
381
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
-
import { mkdtempSync, writeFileSync, rmSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { tmpdir } from 'os';
|
|
5
|
-
import { buildIgnoreSets, defaultIgnoreSets, isPathIgnored, isRelPathIgnored, parseGitignoreContent, } from '../ignore-matcher.js';
|
|
6
|
-
const tempDirs = [];
|
|
7
|
-
function makeTempDir(prefix) {
|
|
8
|
-
const dir = mkdtempSync(join(tmpdir(), `runwork-ignore-${prefix}-`));
|
|
9
|
-
tempDirs.push(dir);
|
|
10
|
-
return dir;
|
|
11
|
-
}
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
for (const dir of tempDirs) {
|
|
14
|
-
try {
|
|
15
|
-
rmSync(dir, { recursive: true, force: true });
|
|
16
|
-
}
|
|
17
|
-
catch { /* best effort */ }
|
|
18
|
-
}
|
|
19
|
-
tempDirs.length = 0;
|
|
20
|
-
});
|
|
21
|
-
describe('defaultIgnoreSets()', () => {
|
|
22
|
-
it('includes the historical defaults plus .bun-cache', () => {
|
|
23
|
-
const sets = defaultIgnoreSets();
|
|
24
|
-
expect(sets.dirs.has('.git')).toBe(true);
|
|
25
|
-
expect(sets.dirs.has('node_modules')).toBe(true);
|
|
26
|
-
expect(sets.dirs.has('.runwork')).toBe(true);
|
|
27
|
-
expect(sets.dirs.has('.bun-cache')).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
it('excludes every .runwork/ state file from deploy/dev auto-commit staging', () => {
|
|
30
|
-
// commitWorkingTree() filters untracked files through isRelPathIgnored,
|
|
31
|
-
// so any path with a .runwork segment must be excluded -- including the
|
|
32
|
-
// local deploy-state/status files written before the detached child
|
|
33
|
-
// commits (Codex review P1).
|
|
34
|
-
const sets = defaultIgnoreSets();
|
|
35
|
-
for (const p of [
|
|
36
|
-
'.runwork/deploy-status.json',
|
|
37
|
-
'.runwork/last-deploy.json',
|
|
38
|
-
'.runwork/deploy-stdout.log',
|
|
39
|
-
'.runwork/dev-session.json',
|
|
40
|
-
'.runwork/setup.json',
|
|
41
|
-
'.runwork/preview-url',
|
|
42
|
-
]) {
|
|
43
|
-
expect(isRelPathIgnored(p, sets), `${p} must be ignored`).toBe(true);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
it('includes .dev.vars and .env file defaults', () => {
|
|
47
|
-
const sets = defaultIgnoreSets();
|
|
48
|
-
expect(sets.files.has('.dev.vars')).toBe(true);
|
|
49
|
-
expect(sets.files.has('.env')).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
it('includes .log extension default', () => {
|
|
52
|
-
const sets = defaultIgnoreSets();
|
|
53
|
-
expect(sets.extensions.has('.log')).toBe(true);
|
|
54
|
-
});
|
|
55
|
-
it('returns independent set instances on each call', () => {
|
|
56
|
-
const a = defaultIgnoreSets();
|
|
57
|
-
const b = defaultIgnoreSets();
|
|
58
|
-
a.dirs.add('mutated');
|
|
59
|
-
expect(b.dirs.has('mutated')).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('parseGitignoreContent()', () => {
|
|
63
|
-
it('parses simple basename patterns', () => {
|
|
64
|
-
const { dirs, files } = parseGitignoreContent('node_modules\n.cache\n');
|
|
65
|
-
expect(dirs.has('node_modules')).toBe(true);
|
|
66
|
-
expect(dirs.has('.cache')).toBe(true);
|
|
67
|
-
expect(files.has('node_modules')).toBe(true);
|
|
68
|
-
expect(files.has('.cache')).toBe(true);
|
|
69
|
-
});
|
|
70
|
-
it('treats trailing-slash patterns as directory-only', () => {
|
|
71
|
-
const { dirs, files } = parseGitignoreContent('dist/\n');
|
|
72
|
-
expect(dirs.has('dist')).toBe(true);
|
|
73
|
-
expect(files.has('dist')).toBe(false);
|
|
74
|
-
});
|
|
75
|
-
it('strips leading slash on top-level patterns', () => {
|
|
76
|
-
const { dirs } = parseGitignoreContent('/dist/\n');
|
|
77
|
-
expect(dirs.has('dist')).toBe(true);
|
|
78
|
-
});
|
|
79
|
-
it('skips comments and blank lines', () => {
|
|
80
|
-
const { dirs } = parseGitignoreContent('# a comment\n\n \n.cache\n');
|
|
81
|
-
expect(dirs.size).toBe(1);
|
|
82
|
-
expect(dirs.has('.cache')).toBe(true);
|
|
83
|
-
});
|
|
84
|
-
it('skips negation patterns', () => {
|
|
85
|
-
const { dirs } = parseGitignoreContent('!keepme\n.cache\n');
|
|
86
|
-
expect(dirs.has('keepme')).toBe(false);
|
|
87
|
-
expect(dirs.has('.cache')).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
it('skips patterns containing glob characters', () => {
|
|
90
|
-
const { dirs, files } = parseGitignoreContent('*.local\nfoo[1].txt\nq?.tmp\n.cache\n');
|
|
91
|
-
expect(dirs.has('*.local')).toBe(false);
|
|
92
|
-
expect(files.has('*.local')).toBe(false);
|
|
93
|
-
expect(dirs.has('foo[1].txt')).toBe(false);
|
|
94
|
-
expect(dirs.has('q?.tmp')).toBe(false);
|
|
95
|
-
expect(dirs.has('.cache')).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
it('skips patterns with internal path separators', () => {
|
|
98
|
-
const { dirs } = parseGitignoreContent('src/generated\n.runwork/types\n.cache\n');
|
|
99
|
-
expect(dirs.has('src/generated')).toBe(false);
|
|
100
|
-
expect(dirs.has('.runwork/types')).toBe(false);
|
|
101
|
-
expect(dirs.has('.cache')).toBe(true);
|
|
102
|
-
});
|
|
103
|
-
it('parses a realistic runwork-app .gitignore without throwing', () => {
|
|
104
|
-
const content = [
|
|
105
|
-
'# Logs',
|
|
106
|
-
'logs',
|
|
107
|
-
'*.log',
|
|
108
|
-
'',
|
|
109
|
-
'node_modules',
|
|
110
|
-
'dist',
|
|
111
|
-
'dist-ssr',
|
|
112
|
-
'*.local',
|
|
113
|
-
'.idea',
|
|
114
|
-
'.DS_Store',
|
|
115
|
-
'.wrangler',
|
|
116
|
-
'.dev.vars*',
|
|
117
|
-
'.env.*',
|
|
118
|
-
'data/',
|
|
119
|
-
'.data/',
|
|
120
|
-
'*.tsbuildinfo',
|
|
121
|
-
'.eslintcache',
|
|
122
|
-
'.runwork/types/',
|
|
123
|
-
'.runwork/.credentials',
|
|
124
|
-
'.runwork/logs.txt',
|
|
125
|
-
'SKILL.md',
|
|
126
|
-
'.bun-cache/',
|
|
127
|
-
].join('\n');
|
|
128
|
-
const { dirs } = parseGitignoreContent(content);
|
|
129
|
-
expect(dirs.has('node_modules')).toBe(true);
|
|
130
|
-
expect(dirs.has('dist')).toBe(true);
|
|
131
|
-
expect(dirs.has('dist-ssr')).toBe(true);
|
|
132
|
-
expect(dirs.has('.idea')).toBe(true);
|
|
133
|
-
expect(dirs.has('.wrangler')).toBe(true);
|
|
134
|
-
expect(dirs.has('.bun-cache')).toBe(true);
|
|
135
|
-
expect(dirs.has('.eslintcache')).toBe(true);
|
|
136
|
-
expect(dirs.has('SKILL.md')).toBe(true);
|
|
137
|
-
// Patterns containing a slash, glob, or starting with `!` are not
|
|
138
|
-
// representable as basename matches and must be silently skipped.
|
|
139
|
-
expect(dirs.has('.runwork/types')).toBe(false);
|
|
140
|
-
expect(dirs.has('*.log')).toBe(false);
|
|
141
|
-
expect(dirs.has('.dev.vars*')).toBe(false);
|
|
142
|
-
expect(dirs.has('.env.*')).toBe(false);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
describe('buildIgnoreSets()', () => {
|
|
146
|
-
it('falls back to defaults when no .gitignore is present', () => {
|
|
147
|
-
const dir = makeTempDir('no-gitignore');
|
|
148
|
-
const sets = buildIgnoreSets(dir);
|
|
149
|
-
expect(sets.dirs.has('.bun-cache')).toBe(true);
|
|
150
|
-
expect(sets.dirs.has('node_modules')).toBe(true);
|
|
151
|
-
// No project-specific entries get added.
|
|
152
|
-
expect(sets.dirs.has('dist')).toBe(false);
|
|
153
|
-
});
|
|
154
|
-
it('merges defaults with patterns parsed from .gitignore', () => {
|
|
155
|
-
const dir = makeTempDir('with-gitignore');
|
|
156
|
-
writeFileSync(join(dir, '.gitignore'), 'dist/\n.next/\ncoverage\n');
|
|
157
|
-
const sets = buildIgnoreSets(dir);
|
|
158
|
-
// Defaults still present.
|
|
159
|
-
expect(sets.dirs.has('.bun-cache')).toBe(true);
|
|
160
|
-
// Project-specific entries added.
|
|
161
|
-
expect(sets.dirs.has('dist')).toBe(true);
|
|
162
|
-
expect(sets.dirs.has('.next')).toBe(true);
|
|
163
|
-
expect(sets.dirs.has('coverage')).toBe(true);
|
|
164
|
-
});
|
|
165
|
-
it('returns defaults if .gitignore is unreadable', () => {
|
|
166
|
-
const dir = makeTempDir('bad-gitignore');
|
|
167
|
-
// Create a directory at the .gitignore path so readFileSync fails.
|
|
168
|
-
// Easiest cross-platform way: pass a directory path. We just write
|
|
169
|
-
// nothing here and skip — but verify no throw and defaults applied.
|
|
170
|
-
// (The unreadable path is exercised by buildIgnoreSets's try/catch.)
|
|
171
|
-
const sets = buildIgnoreSets(dir);
|
|
172
|
-
expect(sets.dirs.has('.bun-cache')).toBe(true);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
describe('isPathIgnored()', () => {
|
|
176
|
-
const sets = defaultIgnoreSets();
|
|
177
|
-
it('ignores well-known cache directories by basename', () => {
|
|
178
|
-
expect(isPathIgnored('.bun-cache', sets)).toBe(true);
|
|
179
|
-
expect(isPathIgnored('/abs/path/.bun-cache', sets)).toBe(true);
|
|
180
|
-
expect(isPathIgnored('./project/node_modules', sets)).toBe(true);
|
|
181
|
-
});
|
|
182
|
-
it('ignores .dev.vars and .env files exactly (not substrings)', () => {
|
|
183
|
-
expect(isPathIgnored('.dev.vars', sets)).toBe(true);
|
|
184
|
-
expect(isPathIgnored('.env', sets)).toBe(true);
|
|
185
|
-
expect(isPathIgnored('.env.example', sets)).toBe(false);
|
|
186
|
-
expect(isPathIgnored('.env.local', sets)).toBe(false);
|
|
187
|
-
});
|
|
188
|
-
it('ignores files whose extension is in the extension set', () => {
|
|
189
|
-
expect(isPathIgnored('debug.log', sets)).toBe(true);
|
|
190
|
-
expect(isPathIgnored('/var/log/app.log', sets)).toBe(true);
|
|
191
|
-
expect(isPathIgnored('app.txt', sets)).toBe(false);
|
|
192
|
-
});
|
|
193
|
-
it('does not ignore ordinary source files', () => {
|
|
194
|
-
expect(isPathIgnored('src/index.ts', sets)).toBe(false);
|
|
195
|
-
expect(isPathIgnored('package.json', sets)).toBe(false);
|
|
196
|
-
expect(isPathIgnored('Makefile', sets)).toBe(false);
|
|
197
|
-
});
|
|
198
|
-
it('honors merged sets from buildIgnoreSets', () => {
|
|
199
|
-
const dir = makeTempDir('honors');
|
|
200
|
-
writeFileSync(join(dir, '.gitignore'), 'dist/\n');
|
|
201
|
-
const merged = buildIgnoreSets(dir);
|
|
202
|
-
expect(isPathIgnored('/repo/dist', merged)).toBe(true);
|
|
203
|
-
expect(isPathIgnored('/repo/src', merged)).toBe(false);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { shouldOutputJson, jsonOut, jsonLine } from '../output.js';
|
|
3
|
-
describe('shouldOutputJson', () => {
|
|
4
|
-
it('returns true when json flag is true', () => {
|
|
5
|
-
expect(shouldOutputJson(true)).toBe(true);
|
|
6
|
-
});
|
|
7
|
-
it('returns false when json flag is false (--no-json)', () => {
|
|
8
|
-
expect(shouldOutputJson(false)).toBe(false);
|
|
9
|
-
});
|
|
10
|
-
it('returns false when undefined and stdout is TTY', () => {
|
|
11
|
-
const original = process.stdout.isTTY;
|
|
12
|
-
Object.defineProperty(process.stdout, 'isTTY', { value: true, writable: true });
|
|
13
|
-
expect(shouldOutputJson(undefined)).toBe(false);
|
|
14
|
-
Object.defineProperty(process.stdout, 'isTTY', { value: original, writable: true });
|
|
15
|
-
});
|
|
16
|
-
it('returns true when undefined and stdout is not TTY', () => {
|
|
17
|
-
const original = process.stdout.isTTY;
|
|
18
|
-
Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true });
|
|
19
|
-
expect(shouldOutputJson(undefined)).toBe(true);
|
|
20
|
-
Object.defineProperty(process.stdout, 'isTTY', { value: original, writable: true });
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
describe('jsonOut', () => {
|
|
24
|
-
it('writes JSON to stdout with newline', () => {
|
|
25
|
-
const spy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
26
|
-
jsonOut({ hello: 'world' });
|
|
27
|
-
expect(spy).toHaveBeenCalledWith('{"hello":"world"}\n');
|
|
28
|
-
spy.mockRestore();
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe('jsonLine', () => {
|
|
32
|
-
it('writes a single NDJSON line', () => {
|
|
33
|
-
const spy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
34
|
-
jsonLine({ event: 'test', ts: 123 });
|
|
35
|
-
expect(spy).toHaveBeenCalledWith('{"event":"test","ts":123}\n');
|
|
36
|
-
spy.mockRestore();
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { promptSelect } from '../prompt.js';
|
|
3
|
-
describe('promptSelect', () => {
|
|
4
|
-
it('throws when no choices available', async () => {
|
|
5
|
-
await expect(promptSelect('Pick:', [])).rejects.toThrow('No choices available');
|
|
6
|
-
});
|
|
7
|
-
it('returns single choice without prompting', async () => {
|
|
8
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
9
|
-
const choice = { label: 'Only option', value: 'only' };
|
|
10
|
-
const result = await promptSelect('Pick:', [choice]);
|
|
11
|
-
expect(result).toBe(choice);
|
|
12
|
-
consoleSpy.mockRestore();
|
|
13
|
-
});
|
|
14
|
-
it('auto-selects preserves all properties on single choice', async () => {
|
|
15
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
16
|
-
const choice = { label: 'Test', id: 'abc', extra: true };
|
|
17
|
-
const result = await promptSelect('Select', [choice]);
|
|
18
|
-
expect(result).toBe(choice);
|
|
19
|
-
consoleSpy.mockRestore();
|
|
20
|
-
});
|
|
21
|
-
it('logs message and choice label when single choice', async () => {
|
|
22
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
23
|
-
await promptSelect('Pick one', [{ label: 'Only Option', value: 42 }]);
|
|
24
|
-
expect(consoleSpy).toHaveBeenCalledWith('Pick one: Only Option');
|
|
25
|
-
consoleSpy.mockRestore();
|
|
26
|
-
});
|
|
27
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|