@askrjs/cli 0.0.2 → 0.0.4
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/README.md +46 -7
- package/dist/add.d.ts +16 -0
- package/dist/add.js +558 -1
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +43 -16
- package/dist/create.d.ts +5 -0
- package/dist/create.js +104 -26
- package/dist/directory-swap-DWoHtx7C.js +35 -0
- package/dist/discovery-Difb7Y4G.js +323 -0
- package/dist/generate.d.ts +5 -0
- package/dist/generate.js +461 -0
- package/dist/is-direct-execution-Cdlr-ZUl.js +0 -2
- package/dist/openapi.d.ts +25 -0
- package/dist/openapi.js +49 -16
- package/dist/planner-VAj7qlxr.js +285 -0
- package/dist/range-YUs9eimn.js +125 -0
- package/dist/registry-CyrRHRG5.js +209 -0
- package/dist/skills/askr-dashboard-charts/SKILL.md +61 -44
- package/dist/skills/askr-dashboard-charts/agents/openai.yaml +2 -2
- package/dist/skills/askr-ssr-ssg/SKILL.md +3 -1
- package/dist/{skills-Bh0mEhIx.js → skills-B7CbWur9.js} +89 -26
- package/dist/skills.d.ts +18 -0
- package/dist/skills.js +1 -1
- package/dist/specification-DXnDOC-0.js +49 -0
- package/dist/ssg-config.d.ts +41 -0
- package/dist/ssg-config.js +1 -0
- package/dist/ssg.d.ts +61 -0
- package/dist/ssg.js +360 -24
- package/dist/templates/full-stack/AGENTS.md +0 -1
- package/dist/templates/full-stack/README.md +0 -1
- package/dist/templates/full-stack/index.html +0 -1
- package/dist/templates/full-stack/package.json +5 -3
- package/dist/templates/full-stack/server.ts +5 -9
- package/dist/templates/full-stack/src/action-authorizations.ts +2 -2
- package/dist/templates/full-stack/src/actions/create-message.ts +5 -5
- package/dist/templates/full-stack/src/i18n.ts +4 -4
- package/dist/templates/full-stack/src/main.tsx +4 -5
- package/dist/templates/full-stack/src/pages/home.tsx +5 -5
- package/dist/templates/full-stack/src/pages/layout.tsx +15 -5
- package/dist/templates/full-stack/src/pages/not-found.tsx +5 -2
- package/dist/templates/full-stack/src/routes.tsx +11 -12
- package/dist/templates/full-stack/src/schemas.ts +1 -2
- package/dist/templates/full-stack/src/server/action-registry.ts +5 -10
- package/dist/templates/full-stack/src/server/actions/create-message.ts +4 -4
- package/dist/templates/full-stack/src/server/app.ts +67 -57
- package/dist/templates/full-stack/src/server/dependencies.ts +10 -10
- package/dist/templates/full-stack/src/server/entry-server.ts +3 -3
- package/dist/templates/full-stack/src/telemetry.ts +1 -2
- package/dist/templates/full-stack/tests/actions/create-message.test.ts +7 -8
- package/dist/templates/full-stack/tsconfig.json +1 -2
- package/dist/templates/full-stack/vite.config.ts +5 -6
- package/dist/templates/spa/AGENTS.md +1 -1
- package/dist/templates/spa/package.json +3 -4
- package/dist/templates/spa/src/adapters/operations-client.ts +7 -2
- package/dist/templates/spa/src/pages/app/_routes.tsx +2 -0
- package/dist/templates/spa/src/pages/app/admin-home.tsx +21 -3
- package/dist/templates/spa/src/pages/public/_routes.tsx +2 -0
- package/dist/templates/spa/src/styles.css +1 -1
- package/dist/templates/spa/tsconfig.json +2 -1
- package/dist/templates/spa/vitest.config.ts +0 -4
- package/dist/templates/ssg/README.md +1 -1
- package/dist/templates/ssg/package.json +1 -1
- package/dist/templates/ssg/ssg.config.ts +7 -0
- package/dist/templates/ssg/tests/ssg-config.test.ts +5 -3
- package/dist/templates/ssg/tsconfig.json +2 -1
- package/dist/templates/ssg/vitest.config.ts +0 -4
- package/dist/templates/ssr/package.json +2 -2
- package/dist/templates/ssr/server.ts +4 -10
- package/dist/templates/ssr/src/entry-server.tsx +6 -4
- package/dist/templates/startkit/package.json +2 -2
- package/dist/templates/startkit/src/lib/mock-data.ts +17 -39
- package/dist/templates/startkit/tsconfig.json +2 -1
- package/dist/update.d.ts +41 -0
- package/dist/update.js +282 -2
- package/dist/writer-D8qe_7ud.js +226 -0
- package/package.json +23 -22
- package/dist/add-DqhqRzBQ.js +0 -555
- package/dist/add-DqhqRzBQ.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/create.js.map +0 -1
- package/dist/is-direct-execution-Cdlr-ZUl.js.map +0 -1
- package/dist/openapi.js.map +0 -1
- package/dist/skills-Bh0mEhIx.js.map +0 -1
- package/dist/ssg.js.map +0 -1
- package/dist/templates/full-stack/src/vite-server.d.ts +0 -6
- package/dist/templates/spa/src/styles.d.ts +0 -1
- package/dist/templates/ssg/src/vite-env.d.ts +0 -5
- package/dist/templates/ssr/src/vite-server.d.ts +0 -7
- package/dist/templates/startkit/src/vite-env.d.ts +0 -5
- package/dist/update-DyW1na5V.js +0 -1140
- package/dist/update-DyW1na5V.js.map +0 -1
- package/skills/askr-accessibility/SKILL.md +0 -68
- package/skills/askr-accessibility/agents/openai.yaml +0 -4
- package/skills/askr-agent-execution/SKILL.md +0 -85
- package/skills/askr-agent-execution/agents/openai.yaml +0 -4
- package/skills/askr-agent-workflows/SKILL.md +0 -57
- package/skills/askr-agent-workflows/agents/openai.yaml +0 -4
- package/skills/askr-api-integration/SKILL.md +0 -79
- package/skills/askr-api-integration/agents/openai.yaml +0 -4
- package/skills/askr-app-builder/SKILL.md +0 -99
- package/skills/askr-app-builder/agents/openai.yaml +0 -4
- package/skills/askr-auth-access/SKILL.md +0 -74
- package/skills/askr-auth-access/agents/openai.yaml +0 -4
- package/skills/askr-cli-vite/SKILL.md +0 -76
- package/skills/askr-cli-vite/agents/openai.yaml +0 -4
- package/skills/askr-dashboard-charts/SKILL.md +0 -82
- package/skills/askr-dashboard-charts/agents/openai.yaml +0 -4
- package/skills/askr-design-system/SKILL.md +0 -86
- package/skills/askr-design-system/agents/openai.yaml +0 -4
- package/skills/askr-env-config/SKILL.md +0 -74
- package/skills/askr-env-config/agents/openai.yaml +0 -4
- package/skills/askr-error-loading-empty/SKILL.md +0 -89
- package/skills/askr-error-loading-empty/agents/openai.yaml +0 -4
- package/skills/askr-file-upload-artifacts/SKILL.md +0 -77
- package/skills/askr-file-upload-artifacts/agents/openai.yaml +0 -4
- package/skills/askr-forms-tables-crud/SKILL.md +0 -98
- package/skills/askr-forms-tables-crud/agents/openai.yaml +0 -4
- package/skills/askr-mental-model/SKILL.md +0 -109
- package/skills/askr-mental-model/agents/openai.yaml +0 -4
- package/skills/askr-migration-react/SKILL.md +0 -81
- package/skills/askr-migration-react/agents/openai.yaml +0 -4
- package/skills/askr-observability-debugging/SKILL.md +0 -83
- package/skills/askr-observability-debugging/agents/openai.yaml +0 -4
- package/skills/askr-project-structure/SKILL.md +0 -79
- package/skills/askr-project-structure/agents/openai.yaml +0 -4
- package/skills/askr-query-mutation/SKILL.md +0 -115
- package/skills/askr-query-mutation/agents/openai.yaml +0 -4
- package/skills/askr-realtime-streaming/SKILL.md +0 -76
- package/skills/askr-realtime-streaming/agents/openai.yaml +0 -4
- package/skills/askr-resources-data/SKILL.md +0 -100
- package/skills/askr-resources-data/agents/openai.yaml +0 -4
- package/skills/askr-routing-layouts/SKILL.md +0 -107
- package/skills/askr-routing-layouts/agents/openai.yaml +0 -4
- package/skills/askr-runtime-reactivity/SKILL.md +0 -92
- package/skills/askr-runtime-reactivity/agents/openai.yaml +0 -4
- package/skills/askr-ssr-ssg/SKILL.md +0 -76
- package/skills/askr-ssr-ssg/agents/openai.yaml +0 -4
- package/skills/askr-testing-determinism/SKILL.md +0 -75
- package/skills/askr-testing-determinism/agents/openai.yaml +0 -4
- package/skills/askr-theming/SKILL.md +0 -86
- package/skills/askr-theming/agents/openai.yaml +0 -4
- package/skills/askr-ui-composition/SKILL.md +0 -92
- package/skills/askr-ui-composition/agents/openai.yaml +0 -4
- package/templates/full-stack/AGENTS.md +0 -12
- package/templates/full-stack/README.md +0 -18
- package/templates/full-stack/gitignore.template +0 -4
- package/templates/full-stack/index.html +0 -14
- package/templates/full-stack/package.json +0 -37
- package/templates/full-stack/server.ts +0 -13
- package/templates/full-stack/src/action-authorizations.ts +0 -10
- package/templates/full-stack/src/actions/create-message.ts +0 -10
- package/templates/full-stack/src/i18n.ts +0 -8
- package/templates/full-stack/src/main.tsx +0 -12
- package/templates/full-stack/src/pages/home.tsx +0 -25
- package/templates/full-stack/src/pages/layout.tsx +0 -33
- package/templates/full-stack/src/pages/not-found.tsx +0 -4
- package/templates/full-stack/src/routes.tsx +0 -20
- package/templates/full-stack/src/schemas.ts +0 -11
- package/templates/full-stack/src/server/action-registry.ts +0 -12
- package/templates/full-stack/src/server/actions/create-message.ts +0 -14
- package/templates/full-stack/src/server/app.ts +0 -66
- package/templates/full-stack/src/server/dependencies.ts +0 -55
- package/templates/full-stack/src/server/entry-server.ts +0 -8
- package/templates/full-stack/src/telemetry.ts +0 -4
- package/templates/full-stack/src/vite-server.d.ts +0 -6
- package/templates/full-stack/tests/actions/create-message.test.ts +0 -13
- package/templates/full-stack/tsconfig.json +0 -16
- package/templates/full-stack/vite.config.ts +0 -11
- package/templates/spa/AGENTS.md +0 -60
- package/templates/spa/README.md +0 -96
- package/templates/spa/gitignore.template +0 -16
- package/templates/spa/index.html +0 -13
- package/templates/spa/package.json +0 -35
- package/templates/spa/src/adapters/operations-client.ts +0 -90
- package/templates/spa/src/components/shared/metric-card.tsx +0 -27
- package/templates/spa/src/components/shared/status-badge.tsx +0 -24
- package/templates/spa/src/features/operations/operations.query.ts +0 -9
- package/templates/spa/src/main.tsx +0 -10
- package/templates/spa/src/pages/_layout.tsx +0 -9
- package/templates/spa/src/pages/_routes.tsx +0 -27
- package/templates/spa/src/pages/app/_layout.tsx +0 -94
- package/templates/spa/src/pages/app/_routes.tsx +0 -10
- package/templates/spa/src/pages/app/admin-home.tsx +0 -152
- package/templates/spa/src/pages/app/agent-runs.tsx +0 -93
- package/templates/spa/src/pages/app/settings.tsx +0 -91
- package/templates/spa/src/pages/auth/_layout.tsx +0 -38
- package/templates/spa/src/pages/auth/_routes.tsx +0 -6
- package/templates/spa/src/pages/auth/login.tsx +0 -63
- package/templates/spa/src/pages/not-found.tsx +0 -22
- package/templates/spa/src/pages/public/_layout.tsx +0 -47
- package/templates/spa/src/pages/public/_routes.tsx +0 -6
- package/templates/spa/src/pages/public/home.tsx +0 -143
- package/templates/spa/src/shared/format.ts +0 -12
- package/templates/spa/src/shared/navigation.ts +0 -15
- package/templates/spa/src/styles/components.css +0 -49
- package/templates/spa/src/styles/layout.css +0 -192
- package/templates/spa/src/styles/reset.css +0 -44
- package/templates/spa/src/styles/theme.css +0 -57
- package/templates/spa/src/styles/tokens.css +0 -20
- package/templates/spa/src/styles.css +0 -7
- package/templates/spa/src/styles.d.ts +0 -1
- package/templates/spa/tests/app.test.tsx +0 -37
- package/templates/spa/tests/components/shared.test.tsx +0 -12
- package/templates/spa/tests/resources.test.ts +0 -22
- package/templates/spa/tsconfig.json +0 -20
- package/templates/spa/tsconfig.node.json +0 -10
- package/templates/spa/vite.config.ts +0 -24
- package/templates/spa/vitest.config.ts +0 -17
- package/templates/ssg/AGENTS.md +0 -55
- package/templates/ssg/README.md +0 -39
- package/templates/ssg/gitignore.template +0 -17
- package/templates/ssg/index.html +0 -13
- package/templates/ssg/package.json +0 -35
- package/templates/ssg/src/app.tsx +0 -14
- package/templates/ssg/src/components/badge.tsx +0 -3
- package/templates/ssg/src/components/counter.tsx +0 -30
- package/templates/ssg/src/components/site-shell.tsx +0 -111
- package/templates/ssg/src/jsx.d.ts +0 -23
- package/templates/ssg/src/main.tsx +0 -8
- package/templates/ssg/src/pages/about.tsx +0 -82
- package/templates/ssg/src/pages/content.tsx +0 -63
- package/templates/ssg/src/pages/example.tsx +0 -97
- package/templates/ssg/src/pages/home.tsx +0 -51
- package/templates/ssg/src/resources/user.ts +0 -15
- package/templates/ssg/src/routes.tsx +0 -15
- package/templates/ssg/src/styles.css +0 -89
- package/templates/ssg/src/vite-env.d.ts +0 -5
- package/templates/ssg/ssg.config.ts +0 -34
- package/templates/ssg/tests/app.test.tsx +0 -19
- package/templates/ssg/tests/components/counter.test.tsx +0 -18
- package/templates/ssg/tests/resources.test.ts +0 -11
- package/templates/ssg/tests/ssg-config.test.ts +0 -11
- package/templates/ssg/tsconfig.json +0 -20
- package/templates/ssg/tsconfig.node.json +0 -10
- package/templates/ssg/vite.config.ts +0 -24
- package/templates/ssg/vitest.config.ts +0 -17
- package/templates/ssr/AGENTS.md +0 -28
- package/templates/ssr/README.md +0 -38
- package/templates/ssr/gitignore.template +0 -16
- package/templates/ssr/index.html +0 -13
- package/templates/ssr/package.json +0 -34
- package/templates/ssr/server.ts +0 -16
- package/templates/ssr/src/app.tsx +0 -21
- package/templates/ssr/src/components/counter.tsx +0 -26
- package/templates/ssr/src/entry-server.tsx +0 -10
- package/templates/ssr/src/main.tsx +0 -11
- package/templates/ssr/src/pages/about.tsx +0 -75
- package/templates/ssr/src/pages/example.tsx +0 -113
- package/templates/ssr/src/pages/home.tsx +0 -56
- package/templates/ssr/src/resources/user.ts +0 -15
- package/templates/ssr/src/routes.tsx +0 -13
- package/templates/ssr/src/styles.css +0 -148
- package/templates/ssr/src/vite-server.d.ts +0 -7
- package/templates/ssr/tests/app.test.tsx +0 -24
- package/templates/ssr/tests/components/counter.test.tsx +0 -13
- package/templates/ssr/tests/resources.test.ts +0 -11
- package/templates/ssr/tsconfig.json +0 -22
- package/templates/ssr/tsconfig.node.json +0 -10
- package/templates/ssr/tsconfig.server.json +0 -15
- package/templates/ssr/vite.config.ts +0 -25
- package/templates/ssr/vitest.config.ts +0 -13
- package/templates/startkit/AGENTS.md +0 -77
- package/templates/startkit/README.md +0 -192
- package/templates/startkit/gitignore.template +0 -16
- package/templates/startkit/index.html +0 -13
- package/templates/startkit/package.json +0 -35
- package/templates/startkit/src/components/app-header.tsx +0 -93
- package/templates/startkit/src/components/app-sidebar.tsx +0 -95
- package/templates/startkit/src/components/data-table.tsx +0 -80
- package/templates/startkit/src/components/empty-state.tsx +0 -13
- package/templates/startkit/src/components/page-header.tsx +0 -23
- package/templates/startkit/src/components/stat-card.tsx +0 -24
- package/templates/startkit/src/features/accounts/account-filters.tsx +0 -61
- package/templates/startkit/src/features/accounts/account-table.tsx +0 -79
- package/templates/startkit/src/lib/format.ts +0 -31
- package/templates/startkit/src/lib/mock-data.ts +0 -441
- package/templates/startkit/src/lib/routes.ts +0 -116
- package/templates/startkit/src/main.tsx +0 -12
- package/templates/startkit/src/pages/_layout.tsx +0 -56
- package/templates/startkit/src/pages/auth/_layout.tsx +0 -7
- package/templates/startkit/src/pages/auth/login.tsx +0 -129
- package/templates/startkit/src/pages/home.tsx +0 -83
- package/templates/startkit/src/pages/not-found.tsx +0 -24
- package/templates/startkit/src/pages/workspace/_layout.tsx +0 -17
- package/templates/startkit/src/pages/workspace/accounts/index.tsx +0 -217
- package/templates/startkit/src/pages/workspace/dashboard.tsx +0 -118
- package/templates/startkit/src/pages/workspace/settings.tsx +0 -218
- package/templates/startkit/src/router.tsx +0 -7
- package/templates/startkit/src/routes/auth-config.ts +0 -38
- package/templates/startkit/src/routes/auth.ts +0 -8
- package/templates/startkit/src/routes/index.ts +0 -28
- package/templates/startkit/src/routes/public.ts +0 -8
- package/templates/startkit/src/routes/workspace/accounts.ts +0 -8
- package/templates/startkit/src/routes/workspace/index.ts +0 -14
- package/templates/startkit/src/styles/components.css +0 -396
- package/templates/startkit/src/styles/layout.css +0 -192
- package/templates/startkit/src/styles/reset.css +0 -38
- package/templates/startkit/src/styles/theme.css +0 -76
- package/templates/startkit/src/styles/tokens.css +0 -67
- package/templates/startkit/src/styles.css +0 -7
- package/templates/startkit/src/toast.ts +0 -55
- package/templates/startkit/src/utils/join-classes.ts +0 -4
- package/templates/startkit/src/vite-env.d.ts +0 -5
- package/templates/startkit/tests/app.test.tsx +0 -32
- package/templates/startkit/tests/preferences.test.ts +0 -43
- package/templates/startkit/tests/resources.test.ts +0 -82
- package/templates/startkit/tsconfig.json +0 -20
- package/templates/startkit/tsconfig.node.json +0 -10
- package/templates/startkit/vite.config.ts +0 -32
- package/templates/startkit/vitest.config.ts +0 -13
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"dev": "vp dev",
|
|
7
7
|
"build": "vp build",
|
|
8
8
|
"preview": "vp preview",
|
|
9
|
-
"test": "vp test run",
|
|
9
|
+
"test": "vp test run -c ./vitest.config.ts",
|
|
10
10
|
"test:watch": "vp test --watch",
|
|
11
11
|
"typecheck": "tsc --noEmit",
|
|
12
12
|
"lint": "vp lint .",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@askrjs/vite": ">=0.0.6 <0.1.0",
|
|
27
|
-
"@types/node": "^
|
|
27
|
+
"@types/node": "^20.19.0",
|
|
28
28
|
"jsdom": "^29.1.1",
|
|
29
29
|
"typescript": "^6.0.3",
|
|
30
30
|
"vite-plus": "^0.2.4"
|
|
@@ -173,43 +173,32 @@ function wait(signal?: AbortSignal, delay = 220): Promise<void> {
|
|
|
173
173
|
});
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
function
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
function browserStorage(): Storage | undefined {
|
|
177
|
+
const globalDescriptor = Object.getOwnPropertyDescriptor(
|
|
178
|
+
globalThis,
|
|
179
|
+
'localStorage'
|
|
180
|
+
);
|
|
181
|
+
if (globalDescriptor?.get && globalDescriptor.enumerable === false) {
|
|
182
|
+
return undefined;
|
|
181
183
|
}
|
|
182
|
-
|
|
184
|
+
if (typeof document === 'undefined') return undefined;
|
|
183
185
|
try {
|
|
184
|
-
|
|
185
|
-
getItem?: (storageKey: string) => string | null;
|
|
186
|
-
};
|
|
187
|
-
if (typeof storage.getItem !== 'function') {
|
|
188
|
-
return fallback;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return storage.getItem(key) ?? fallback;
|
|
186
|
+
return document.defaultView?.localStorage;
|
|
192
187
|
} catch {
|
|
193
|
-
return
|
|
188
|
+
return undefined;
|
|
194
189
|
}
|
|
195
190
|
}
|
|
196
191
|
|
|
192
|
+
function readStorage(key: string): string | null {
|
|
193
|
+
const fallback = storageFallback.get(key) ?? null;
|
|
194
|
+
return browserStorage()?.getItem(key) ?? fallback;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
197
|
function writeStorage(key: string, value: string) {
|
|
198
198
|
storageFallback.set(key, value);
|
|
199
199
|
|
|
200
|
-
if (typeof window === 'undefined') {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
200
|
try {
|
|
205
|
-
|
|
206
|
-
setItem?: (storageKey: string, storageValue: string) => void;
|
|
207
|
-
};
|
|
208
|
-
if (typeof storage.setItem !== 'function') {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
storage.setItem(key, value);
|
|
201
|
+
browserStorage()?.setItem(key, value);
|
|
213
202
|
} catch {
|
|
214
203
|
// Ignore storage write failures in non-browser or restricted test envs.
|
|
215
204
|
}
|
|
@@ -218,19 +207,8 @@ function writeStorage(key: string, value: string) {
|
|
|
218
207
|
function removeStorage(key: string) {
|
|
219
208
|
storageFallback.delete(key);
|
|
220
209
|
|
|
221
|
-
if (typeof window === 'undefined') {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
210
|
try {
|
|
226
|
-
|
|
227
|
-
removeItem?: (storageKey: string) => void;
|
|
228
|
-
};
|
|
229
|
-
if (typeof storage.removeItem !== 'function') {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
storage.removeItem(key);
|
|
211
|
+
browserStorage()?.removeItem(key);
|
|
234
212
|
} catch {
|
|
235
213
|
// Ignore storage delete failures in non-browser or restricted test envs.
|
|
236
214
|
}
|
package/dist/update.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/update/types.d.ts
|
|
2
|
+
declare const DEPENDENCY_SECTIONS: readonly ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
|
|
3
|
+
type DependencySection = (typeof DEPENDENCY_SECTIONS)[number];
|
|
4
|
+
interface Packument {
|
|
5
|
+
"dist-tags"?: Record<string, unknown>;
|
|
6
|
+
versions?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
interface ManifestValueEdit {
|
|
9
|
+
manifestPath: string;
|
|
10
|
+
section: DependencySection;
|
|
11
|
+
package: string;
|
|
12
|
+
currentSpecification: string;
|
|
13
|
+
proposedSpecification: string;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/update/writer.d.ts
|
|
17
|
+
interface WriterOptions {
|
|
18
|
+
replace?: (temporaryPath: string, manifestPath: string) => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
declare function writeManifestEdits(edits: ManifestValueEdit[], options?: WriterOptions): Promise<number>;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/update/registry.d.ts
|
|
23
|
+
interface RegistryRequirements {
|
|
24
|
+
specifications?: ReadonlyMap<string, readonly string[]>;
|
|
25
|
+
}
|
|
26
|
+
interface PackumentResults {
|
|
27
|
+
packuments: Map<string, Packument>;
|
|
28
|
+
failures: Map<string, string>;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/bin/update.d.ts
|
|
32
|
+
type CliIo = Pick<Console, "error" | "log">;
|
|
33
|
+
interface UpdateRuntime {
|
|
34
|
+
registry?: (root: string, packageNames: string[], requirements?: RegistryRequirements) => Promise<PackumentResults>;
|
|
35
|
+
writer?: typeof writeManifestEdits;
|
|
36
|
+
}
|
|
37
|
+
declare const runUpdateCli: (args: string[], io?: CliIo, runtime?: UpdateRuntime) => Promise<number>;
|
|
38
|
+
declare const runUpgradeCli: (args: string[], io?: CliIo, runtime?: UpdateRuntime) => Promise<number>;
|
|
39
|
+
declare const runOutdatedCli: (args: string[], io?: CliIo, runtime?: UpdateRuntime) => Promise<number>;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { runOutdatedCli, runUpdateCli, runUpgradeCli };
|
package/dist/update.js
CHANGED
|
@@ -1,3 +1,283 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as
|
|
3
|
-
|
|
2
|
+
import { t as isDirectExecution } from "./is-direct-execution-Cdlr-ZUl.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/bin/update.ts
|
|
5
|
+
function helpText(command) {
|
|
6
|
+
return [
|
|
7
|
+
`askr ${command} - ${command === "outdated" ? "List available dependency updates" : command === "update" ? "Apply safe dependency updates" : "Apply latest peer-compatible dependency upgrades"}`,
|
|
8
|
+
"",
|
|
9
|
+
"Usage:",
|
|
10
|
+
` askr ${command} [packages...] [options]`,
|
|
11
|
+
"",
|
|
12
|
+
"Options:",
|
|
13
|
+
" --cwd <dir> Resolve a project from another directory",
|
|
14
|
+
" --workspace <glob> Select workspace names (repeatable)",
|
|
15
|
+
" --tag <tag> Use one registry dist-tag for selected packages",
|
|
16
|
+
" --json Emit one deterministic JSON object on stdout",
|
|
17
|
+
" --help, -h Show this help message",
|
|
18
|
+
"",
|
|
19
|
+
"Notes:",
|
|
20
|
+
" Package arguments are minimatch patterns and override persistent ignores.",
|
|
21
|
+
command === "outdated" ? " This command reports only and never writes files." : " The command changes package.json files only.",
|
|
22
|
+
" Lockfiles, installs, and lifecycle scripts are never touched."
|
|
23
|
+
].join("\n");
|
|
24
|
+
}
|
|
25
|
+
function optionValue(args, index, option, errors) {
|
|
26
|
+
if (index + 1 >= args.length || args[index + 1].startsWith("-")) {
|
|
27
|
+
errors.push(`Missing value for ${option}`);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return args[index + 1];
|
|
31
|
+
}
|
|
32
|
+
function parseArgs(args) {
|
|
33
|
+
const parsed = {
|
|
34
|
+
cwd: process.cwd(),
|
|
35
|
+
help: false,
|
|
36
|
+
json: false,
|
|
37
|
+
packagePatterns: [],
|
|
38
|
+
workspacePatterns: [],
|
|
39
|
+
errors: []
|
|
40
|
+
};
|
|
41
|
+
let positionalOnly = false;
|
|
42
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
43
|
+
const argument = args[index];
|
|
44
|
+
if (positionalOnly) parsed.packagePatterns.push(argument);
|
|
45
|
+
else if (argument === "--") positionalOnly = true;
|
|
46
|
+
else if (argument === "--help" || argument === "-h") parsed.help = true;
|
|
47
|
+
else if (argument === "--json") parsed.json = true;
|
|
48
|
+
else if (argument === "--cwd") {
|
|
49
|
+
const value = optionValue(args, index, "--cwd", parsed.errors);
|
|
50
|
+
if (value !== null) {
|
|
51
|
+
parsed.cwd = path.resolve(value);
|
|
52
|
+
index += 1;
|
|
53
|
+
}
|
|
54
|
+
} else if (argument.startsWith("--cwd=")) parsed.cwd = path.resolve(argument.slice(6));
|
|
55
|
+
else if (argument === "--workspace") {
|
|
56
|
+
const value = optionValue(args, index, "--workspace", parsed.errors);
|
|
57
|
+
if (value !== null) {
|
|
58
|
+
parsed.workspacePatterns.push(value);
|
|
59
|
+
index += 1;
|
|
60
|
+
}
|
|
61
|
+
} else if (argument.startsWith("--workspace=")) parsed.workspacePatterns.push(argument.slice(12));
|
|
62
|
+
else if (argument === "--tag") {
|
|
63
|
+
const value = optionValue(args, index, "--tag", parsed.errors);
|
|
64
|
+
if (value !== null) {
|
|
65
|
+
parsed.tag = value;
|
|
66
|
+
index += 1;
|
|
67
|
+
}
|
|
68
|
+
} else if (argument.startsWith("--tag=")) parsed.tag = argument.slice(6);
|
|
69
|
+
else if (argument.startsWith("-")) parsed.errors.push(`Unknown option: ${argument}`);
|
|
70
|
+
else parsed.packagePatterns.push(argument);
|
|
71
|
+
}
|
|
72
|
+
if (parsed.tag !== void 0 && (!parsed.tag || /\s/.test(parsed.tag))) parsed.errors.push("--tag must be a non-empty dist-tag without whitespace");
|
|
73
|
+
if (parsed.workspacePatterns.some((pattern) => !pattern)) parsed.errors.push("--workspace patterns must not be empty");
|
|
74
|
+
if (parsed.packagePatterns.some((pattern) => !pattern)) parsed.errors.push("Package patterns must not be empty");
|
|
75
|
+
return parsed;
|
|
76
|
+
}
|
|
77
|
+
function emptySummary() {
|
|
78
|
+
return {
|
|
79
|
+
packages: 0,
|
|
80
|
+
occurrences: 0,
|
|
81
|
+
changedOccurrences: 0,
|
|
82
|
+
current: 0,
|
|
83
|
+
safe: 0,
|
|
84
|
+
breaking: 0,
|
|
85
|
+
local: 0,
|
|
86
|
+
manual: 0,
|
|
87
|
+
error: 0
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function serializableDecision(decision) {
|
|
91
|
+
return {
|
|
92
|
+
package: decision.package,
|
|
93
|
+
selectedTag: decision.selectedTag,
|
|
94
|
+
targetVersion: decision.targetVersion,
|
|
95
|
+
status: decision.status,
|
|
96
|
+
reason: decision.reason,
|
|
97
|
+
occurrences: decision.occurrences.map((occurrence) => ({
|
|
98
|
+
workspace: occurrence.workspace,
|
|
99
|
+
manifest: occurrence.relativeManifestPath,
|
|
100
|
+
section: occurrence.section,
|
|
101
|
+
currentSpecification: occurrence.currentSpecification,
|
|
102
|
+
proposedSpecification: occurrence.proposedSpecification,
|
|
103
|
+
allowedVersion: occurrence.allowedVersion,
|
|
104
|
+
status: occurrence.status,
|
|
105
|
+
reason: occurrence.reason
|
|
106
|
+
}))
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function selectedWorkspaceJson(root, workspaces) {
|
|
110
|
+
return workspaces.map((workspace) => ({
|
|
111
|
+
name: workspace.name,
|
|
112
|
+
path: path.relative(root, workspace.directory).split(path.sep).join("/") || ".",
|
|
113
|
+
manifest: workspace.relativeManifestPath
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
function emitJson(io, options) {
|
|
117
|
+
io.log(JSON.stringify({
|
|
118
|
+
root: options.root,
|
|
119
|
+
workspaces: options.workspaces,
|
|
120
|
+
summary: options.summary,
|
|
121
|
+
decisions: options.decisions.map(serializableDecision),
|
|
122
|
+
applied: { occurrences: options.applied },
|
|
123
|
+
errors: options.errors
|
|
124
|
+
}, null, 2));
|
|
125
|
+
}
|
|
126
|
+
function rangeText(occurrence, command) {
|
|
127
|
+
if (occurrence.proposedSpecification) return `${occurrence.currentSpecification} → ${occurrence.proposedSpecification}`;
|
|
128
|
+
if (occurrence.status === "breaking") return command === "upgrade" ? `${occurrence.currentSpecification} (${occurrence.reason})` : `${occurrence.currentSpecification} (available via askr upgrade)`;
|
|
129
|
+
return `${occurrence.currentSpecification} (${occurrence.reason})`;
|
|
130
|
+
}
|
|
131
|
+
function renderTable(rows) {
|
|
132
|
+
const headings = [
|
|
133
|
+
"Package",
|
|
134
|
+
"Allowed",
|
|
135
|
+
"Latest",
|
|
136
|
+
"Status",
|
|
137
|
+
"Range"
|
|
138
|
+
];
|
|
139
|
+
const widths = headings.map((heading, column) => Math.max(heading.length, ...rows.map((row) => row[column].length)));
|
|
140
|
+
return [headings, ...rows].map((row) => row.map((value, column) => column === row.length - 1 ? value : value.padEnd(widths[column])).join(" "));
|
|
141
|
+
}
|
|
142
|
+
function emitHuman(io, decisions, summary, applied, command) {
|
|
143
|
+
const byWorkspace = /* @__PURE__ */ new Map();
|
|
144
|
+
for (const decision of decisions) for (const occurrence of decision.occurrences) {
|
|
145
|
+
if (occurrence.status === "current") continue;
|
|
146
|
+
const rows = byWorkspace.get(occurrence.workspace) ?? [];
|
|
147
|
+
const row = [
|
|
148
|
+
decision.package,
|
|
149
|
+
occurrence.allowedVersion ?? "-",
|
|
150
|
+
decision.targetVersion ?? "-",
|
|
151
|
+
occurrence.status,
|
|
152
|
+
rangeText(occurrence, command)
|
|
153
|
+
];
|
|
154
|
+
if (!rows.some((existing) => existing.every((value, index) => value === row[index]))) rows.push(row);
|
|
155
|
+
byWorkspace.set(occurrence.workspace, rows);
|
|
156
|
+
}
|
|
157
|
+
if (byWorkspace.size === 0) io.log("All selected dependencies are current.");
|
|
158
|
+
else {
|
|
159
|
+
let first = true;
|
|
160
|
+
for (const [workspace, rows] of byWorkspace) {
|
|
161
|
+
if (!first) io.log("");
|
|
162
|
+
first = false;
|
|
163
|
+
io.log(workspace);
|
|
164
|
+
for (const line of renderTable(rows)) io.log(line);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const counts = [
|
|
168
|
+
"safe",
|
|
169
|
+
"breaking",
|
|
170
|
+
"current",
|
|
171
|
+
"manual",
|
|
172
|
+
"local",
|
|
173
|
+
"error"
|
|
174
|
+
].filter((status) => summary[status] > 0).map((status) => `${summary[status]} ${status}`).join(", ");
|
|
175
|
+
io.log("");
|
|
176
|
+
io.log(`Scanned ${summary.packages} package${summary.packages === 1 ? "" : "s"}${counts ? `: ${counts}` : "."}`);
|
|
177
|
+
if (command !== "outdated") io.log(`Updated ${applied} manifest occurrence${applied === 1 ? "" : "s"}.`);
|
|
178
|
+
}
|
|
179
|
+
function collectEdits(decisions) {
|
|
180
|
+
return decisions.flatMap((decision) => decision.occurrences.flatMap((occurrence) => occurrence.proposedSpecification ? [{
|
|
181
|
+
manifestPath: occurrence.manifestPath,
|
|
182
|
+
section: occurrence.section,
|
|
183
|
+
package: decision.package,
|
|
184
|
+
currentSpecification: occurrence.currentSpecification,
|
|
185
|
+
proposedSpecification: occurrence.proposedSpecification
|
|
186
|
+
}] : []));
|
|
187
|
+
}
|
|
188
|
+
async function defaultRegistry(root, packageNames, requirements) {
|
|
189
|
+
const { fetchPackuments, loadNpmConfiguration } = await import("./registry-CyrRHRG5.js");
|
|
190
|
+
return fetchPackuments(packageNames, await loadNpmConfiguration(root), { requirements });
|
|
191
|
+
}
|
|
192
|
+
async function runDependencyCli(command, args, io = console, runtime = {}) {
|
|
193
|
+
const parsed = parseArgs(args);
|
|
194
|
+
if (parsed.help && parsed.errors.length === 0) {
|
|
195
|
+
io.log(helpText(command));
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
if (parsed.errors.length > 0) {
|
|
199
|
+
for (const error of parsed.errors) io.error(`askr ${command}: ${error}`);
|
|
200
|
+
if (parsed.json) emitJson(io, {
|
|
201
|
+
root: null,
|
|
202
|
+
workspaces: [],
|
|
203
|
+
summary: emptySummary(),
|
|
204
|
+
decisions: [],
|
|
205
|
+
applied: 0,
|
|
206
|
+
errors: parsed.errors
|
|
207
|
+
});
|
|
208
|
+
return 1;
|
|
209
|
+
}
|
|
210
|
+
let root = null;
|
|
211
|
+
let selectedWorkspaces = [];
|
|
212
|
+
try {
|
|
213
|
+
const [{ discoverProject }, { planUpdates }] = await Promise.all([import("./discovery-Difb7Y4G.js"), import("./planner-VAj7qlxr.js")]);
|
|
214
|
+
const project = await discoverProject({
|
|
215
|
+
cwd: parsed.cwd,
|
|
216
|
+
packagePatterns: parsed.packagePatterns,
|
|
217
|
+
workspacePatterns: parsed.workspacePatterns
|
|
218
|
+
});
|
|
219
|
+
root = project.root;
|
|
220
|
+
selectedWorkspaces = project.selectedWorkspaces;
|
|
221
|
+
const lookupNames = project.occurrences.some((occurrence) => occurrence.kind === "fetch") ? project.contextOccurrences.filter((occurrence) => occurrence.registryManaged).map((occurrence) => occurrence.package) : [];
|
|
222
|
+
const registry = runtime.registry ?? defaultRegistry;
|
|
223
|
+
const specifications = /* @__PURE__ */ new Map();
|
|
224
|
+
for (const occurrence of project.contextOccurrences) {
|
|
225
|
+
const entries = specifications.get(occurrence.package) ?? [];
|
|
226
|
+
if (!entries.includes(occurrence.currentSpecification)) entries.push(occurrence.currentSpecification);
|
|
227
|
+
specifications.set(occurrence.package, entries);
|
|
228
|
+
}
|
|
229
|
+
const results = lookupNames.length > 0 ? await registry(project.root, lookupNames, { specifications }) : {
|
|
230
|
+
packuments: /* @__PURE__ */ new Map(),
|
|
231
|
+
failures: /* @__PURE__ */ new Map()
|
|
232
|
+
};
|
|
233
|
+
const plan = planUpdates({
|
|
234
|
+
occurrences: project.occurrences,
|
|
235
|
+
contextOccurrences: project.contextOccurrences,
|
|
236
|
+
packuments: results.packuments,
|
|
237
|
+
failures: results.failures,
|
|
238
|
+
tags: project.policy.tags,
|
|
239
|
+
cliTag: parsed.tag,
|
|
240
|
+
force: command === "upgrade"
|
|
241
|
+
});
|
|
242
|
+
let applied = 0;
|
|
243
|
+
const errors = [];
|
|
244
|
+
if (!plan.hasErrors && command !== "outdated") try {
|
|
245
|
+
applied = await (runtime.writer ?? (await import("./writer-D8qe_7ud.js")).writeManifestEdits)(collectEdits(plan.decisions));
|
|
246
|
+
} catch {
|
|
247
|
+
errors.push("Unable to replace package manifests; completed replacements were rolled back when possible.");
|
|
248
|
+
}
|
|
249
|
+
if (plan.hasErrors) errors.push("One or more registry lookups or package plans failed.");
|
|
250
|
+
if (parsed.json) emitJson(io, {
|
|
251
|
+
root: project.root,
|
|
252
|
+
workspaces: selectedWorkspaceJson(project.root, project.selectedWorkspaces),
|
|
253
|
+
summary: plan.summary,
|
|
254
|
+
decisions: plan.decisions,
|
|
255
|
+
applied,
|
|
256
|
+
errors
|
|
257
|
+
});
|
|
258
|
+
else emitHuman(io, plan.decisions, plan.summary, applied, command);
|
|
259
|
+
for (const error of errors) io.error(`askr ${command}: ${error}`);
|
|
260
|
+
return errors.length > 0 ? 1 : 0;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
const message = error instanceof Error ? error.message : "Update failed.";
|
|
263
|
+
io.error(`askr ${command}: ${message}`);
|
|
264
|
+
if (parsed.json) emitJson(io, {
|
|
265
|
+
root,
|
|
266
|
+
workspaces: root ? selectedWorkspaceJson(root, selectedWorkspaces) : [],
|
|
267
|
+
summary: emptySummary(),
|
|
268
|
+
decisions: [],
|
|
269
|
+
applied: 0,
|
|
270
|
+
errors: [message]
|
|
271
|
+
});
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const runUpdateCli = (args, io = console, runtime = {}) => runDependencyCli("update", args, io, runtime);
|
|
276
|
+
const runUpgradeCli = (args, io = console, runtime = {}) => runDependencyCli("upgrade", args, io, runtime);
|
|
277
|
+
const runOutdatedCli = (args, io = console, runtime = {}) => runDependencyCli("outdated", args, io, runtime);
|
|
278
|
+
async function main() {
|
|
279
|
+
process.exit(await runUpdateCli(process.argv.slice(2)));
|
|
280
|
+
}
|
|
281
|
+
if (isDirectExecution(import.meta.url)) main();
|
|
282
|
+
//#endregion
|
|
283
|
+
export { runOutdatedCli, runUpdateCli, runUpgradeCli };
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
//#region src/update/writer.ts
|
|
5
|
+
function groupEdits(edits) {
|
|
6
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7
|
+
for (const edit of edits) {
|
|
8
|
+
const entries = grouped.get(edit.manifestPath) ?? [];
|
|
9
|
+
entries.push(edit);
|
|
10
|
+
grouped.set(edit.manifestPath, entries);
|
|
11
|
+
}
|
|
12
|
+
for (const entries of grouped.values()) entries.sort((left, right) => left.section.localeCompare(right.section) || left.package.localeCompare(right.package));
|
|
13
|
+
return new Map([...grouped].sort(([left], [right]) => left.localeCompare(right)));
|
|
14
|
+
}
|
|
15
|
+
var JsonTreeParser = class {
|
|
16
|
+
source;
|
|
17
|
+
index = 0;
|
|
18
|
+
constructor(source) {
|
|
19
|
+
this.source = source;
|
|
20
|
+
if (source.charCodeAt(0) === 65279) this.index = 1;
|
|
21
|
+
}
|
|
22
|
+
parse() {
|
|
23
|
+
const node = this.parseValue();
|
|
24
|
+
this.skipWhitespace();
|
|
25
|
+
if (this.index !== this.source.length) throw new Error("Unexpected content after JSON value");
|
|
26
|
+
return node;
|
|
27
|
+
}
|
|
28
|
+
skipWhitespace() {
|
|
29
|
+
while (/[ \t\r\n]/.test(this.source[this.index] ?? "")) this.index += 1;
|
|
30
|
+
}
|
|
31
|
+
parseValue() {
|
|
32
|
+
this.skipWhitespace();
|
|
33
|
+
const token = this.source[this.index];
|
|
34
|
+
if (token === "{") return this.parseObject();
|
|
35
|
+
if (token === "[") return this.parseArray();
|
|
36
|
+
if (token === "\"") return this.parseString();
|
|
37
|
+
return this.parsePrimitive();
|
|
38
|
+
}
|
|
39
|
+
parseString() {
|
|
40
|
+
const start = this.index;
|
|
41
|
+
this.index += 1;
|
|
42
|
+
while (this.index < this.source.length) {
|
|
43
|
+
const token = this.source[this.index];
|
|
44
|
+
if (token === "\\") {
|
|
45
|
+
this.index += 2;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
this.index += 1;
|
|
49
|
+
if (token === "\"") {
|
|
50
|
+
const raw = this.source.slice(start, this.index);
|
|
51
|
+
const value = JSON.parse(raw);
|
|
52
|
+
if (typeof value !== "string") throw new Error("Invalid JSON string");
|
|
53
|
+
return {
|
|
54
|
+
end: this.index,
|
|
55
|
+
kind: "string",
|
|
56
|
+
start,
|
|
57
|
+
value
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw new Error("Unterminated JSON string");
|
|
62
|
+
}
|
|
63
|
+
parseObject() {
|
|
64
|
+
const start = this.index;
|
|
65
|
+
const properties = /* @__PURE__ */ new Map();
|
|
66
|
+
this.index += 1;
|
|
67
|
+
this.skipWhitespace();
|
|
68
|
+
if (this.source[this.index] === "}") {
|
|
69
|
+
this.index += 1;
|
|
70
|
+
return {
|
|
71
|
+
end: this.index,
|
|
72
|
+
kind: "object",
|
|
73
|
+
properties,
|
|
74
|
+
start
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
while (this.index < this.source.length) {
|
|
78
|
+
this.skipWhitespace();
|
|
79
|
+
if (this.source[this.index] !== "\"") throw new Error("Expected a JSON object key");
|
|
80
|
+
const key = this.parseString().value;
|
|
81
|
+
this.skipWhitespace();
|
|
82
|
+
if (this.source[this.index] !== ":") throw new Error("Expected ':' after JSON object key");
|
|
83
|
+
this.index += 1;
|
|
84
|
+
properties.set(key, this.parseValue());
|
|
85
|
+
this.skipWhitespace();
|
|
86
|
+
if (this.source[this.index] === "}") {
|
|
87
|
+
this.index += 1;
|
|
88
|
+
return {
|
|
89
|
+
end: this.index,
|
|
90
|
+
kind: "object",
|
|
91
|
+
properties,
|
|
92
|
+
start
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (this.source[this.index] !== ",") throw new Error("Expected ',' in JSON object");
|
|
96
|
+
this.index += 1;
|
|
97
|
+
}
|
|
98
|
+
throw new Error("Unterminated JSON object");
|
|
99
|
+
}
|
|
100
|
+
parseArray() {
|
|
101
|
+
const start = this.index;
|
|
102
|
+
this.index += 1;
|
|
103
|
+
this.skipWhitespace();
|
|
104
|
+
if (this.source[this.index] === "]") {
|
|
105
|
+
this.index += 1;
|
|
106
|
+
return {
|
|
107
|
+
end: this.index,
|
|
108
|
+
kind: "array",
|
|
109
|
+
start
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
while (this.index < this.source.length) {
|
|
113
|
+
this.parseValue();
|
|
114
|
+
this.skipWhitespace();
|
|
115
|
+
if (this.source[this.index] === "]") {
|
|
116
|
+
this.index += 1;
|
|
117
|
+
return {
|
|
118
|
+
end: this.index,
|
|
119
|
+
kind: "array",
|
|
120
|
+
start
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (this.source[this.index] !== ",") throw new Error("Expected ',' in JSON array");
|
|
124
|
+
this.index += 1;
|
|
125
|
+
}
|
|
126
|
+
throw new Error("Unterminated JSON array");
|
|
127
|
+
}
|
|
128
|
+
parsePrimitive() {
|
|
129
|
+
const start = this.index;
|
|
130
|
+
while (this.index < this.source.length && !/[ \t\r\n,\]}]/.test(this.source[this.index])) this.index += 1;
|
|
131
|
+
if (start === this.index) throw new Error("Expected a JSON value");
|
|
132
|
+
JSON.parse(this.source.slice(start, this.index));
|
|
133
|
+
return {
|
|
134
|
+
end: this.index,
|
|
135
|
+
kind: "other",
|
|
136
|
+
start
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
function renderReplacement(source, edits) {
|
|
141
|
+
let manifest;
|
|
142
|
+
try {
|
|
143
|
+
manifest = new JsonTreeParser(source).parse();
|
|
144
|
+
} catch {
|
|
145
|
+
throw new Error("Manifest changed to invalid JSON before writing.");
|
|
146
|
+
}
|
|
147
|
+
if (manifest.kind !== "object") throw new Error("Manifest changed to invalid JSON before writing.");
|
|
148
|
+
const replacements = [];
|
|
149
|
+
const seen = /* @__PURE__ */ new Set();
|
|
150
|
+
for (const edit of edits) {
|
|
151
|
+
const key = `${edit.section}\u0000${edit.package}`;
|
|
152
|
+
if (seen.has(key)) throw new Error(`Duplicate manifest edit: ${edit.manifestPath}`);
|
|
153
|
+
seen.add(key);
|
|
154
|
+
const current = (manifest.properties?.get(edit.section))?.properties?.get(edit.package);
|
|
155
|
+
if (current?.kind !== "string" || current.value !== edit.currentSpecification) throw new Error(`Manifest changed before writing: ${edit.manifestPath}`);
|
|
156
|
+
replacements.push({
|
|
157
|
+
start: current.start,
|
|
158
|
+
end: current.end,
|
|
159
|
+
value: JSON.stringify(edit.proposedSpecification)
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
let result = source;
|
|
163
|
+
replacements.sort((left, right) => right.start - left.start);
|
|
164
|
+
for (const replacement of replacements) result = `${result.slice(0, replacement.start)}${replacement.value}${result.slice(replacement.end)}`;
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
async function cleanup(paths) {
|
|
168
|
+
await Promise.all(paths.map((filePath) => fs.rm(filePath, { force: true }).catch(() => void 0)));
|
|
169
|
+
}
|
|
170
|
+
async function rollback(replaced) {
|
|
171
|
+
let complete = true;
|
|
172
|
+
for (const staged of [...replaced].reverse()) {
|
|
173
|
+
const rollbackPath = path.join(path.dirname(staged.manifestPath), `.${path.basename(staged.manifestPath)}.askr-rollback-${randomUUID()}`);
|
|
174
|
+
try {
|
|
175
|
+
await fs.writeFile(rollbackPath, staged.original, {
|
|
176
|
+
flag: "wx",
|
|
177
|
+
mode: staged.mode
|
|
178
|
+
});
|
|
179
|
+
await fs.rename(rollbackPath, staged.manifestPath);
|
|
180
|
+
} catch {
|
|
181
|
+
complete = false;
|
|
182
|
+
await fs.rm(rollbackPath, { force: true }).catch(() => void 0);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return complete;
|
|
186
|
+
}
|
|
187
|
+
async function writeManifestEdits(edits, options = {}) {
|
|
188
|
+
if (edits.length === 0) return 0;
|
|
189
|
+
const staged = [];
|
|
190
|
+
const replace = options.replace ?? fs.rename;
|
|
191
|
+
try {
|
|
192
|
+
for (const [manifestPath, manifestEdits] of groupEdits(edits)) {
|
|
193
|
+
const [original, stat] = await Promise.all([fs.readFile(manifestPath), fs.stat(manifestPath)]);
|
|
194
|
+
const replacement = renderReplacement(original.toString("utf8"), manifestEdits);
|
|
195
|
+
const temporaryPath = path.join(path.dirname(manifestPath), `.${path.basename(manifestPath)}.askr-update-${randomUUID()}`);
|
|
196
|
+
await fs.writeFile(temporaryPath, replacement, {
|
|
197
|
+
flag: "wx",
|
|
198
|
+
mode: stat.mode
|
|
199
|
+
});
|
|
200
|
+
staged.push({
|
|
201
|
+
manifestPath,
|
|
202
|
+
original,
|
|
203
|
+
replacement,
|
|
204
|
+
temporaryPath,
|
|
205
|
+
mode: stat.mode
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
await cleanup(staged.map((entry) => entry.temporaryPath));
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
const replaced = [];
|
|
213
|
+
try {
|
|
214
|
+
for (const manifest of staged) {
|
|
215
|
+
await replace(manifest.temporaryPath, manifest.manifestPath);
|
|
216
|
+
replaced.push(manifest);
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
const rollbackComplete = await rollback(replaced);
|
|
220
|
+
await cleanup(staged.map((entry) => entry.temporaryPath));
|
|
221
|
+
throw new Error(rollbackComplete ? "Manifest replacement failed; completed replacements were rolled back." : "Manifest replacement failed and rollback was incomplete.");
|
|
222
|
+
}
|
|
223
|
+
return edits.length;
|
|
224
|
+
}
|
|
225
|
+
//#endregion
|
|
226
|
+
export { writeManifestEdits };
|