flingit 0.0.70 → 0.0.71
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/cli/commands/cron.d.ts +1 -1
- package/dist/cli/commands/cron.d.ts.map +1 -1
- package/dist/cli/commands/cron.js +2 -2
- package/dist/cli/commands/cron.js.map +1 -1
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/email.d.ts +23 -1
- package/dist/cli/commands/email.d.ts.map +1 -1
- package/dist/cli/commands/email.js +123 -17
- package/dist/cli/commands/email.js.map +1 -1
- package/dist/cli/commands/feedback.d.ts +1 -1
- package/dist/cli/commands/feedback.d.ts.map +1 -1
- package/dist/cli/commands/feedback.js.map +1 -1
- package/dist/cli/commands/launch.js.map +1 -1
- package/dist/cli/commands/login.d.ts +1 -1
- package/dist/cli/commands/login.d.ts.map +1 -1
- package/dist/cli/commands/login.js +33 -19
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/logout.js.map +1 -1
- package/dist/cli/commands/logs.d.ts +3 -4
- package/dist/cli/commands/logs.d.ts.map +1 -1
- package/dist/cli/commands/logs.js.map +1 -1
- package/dist/cli/commands/plugin.d.ts +2 -2
- package/dist/cli/commands/plugin.d.ts.map +1 -1
- package/dist/cli/commands/plugin.js +14 -5
- package/dist/cli/commands/plugin.js.map +1 -1
- package/dist/cli/commands/project.d.ts +20 -1
- package/dist/cli/commands/project.d.ts.map +1 -1
- package/dist/cli/commands/project.js +41 -17
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/push.d.ts +2 -1
- package/dist/cli/commands/push.d.ts.map +1 -1
- package/dist/cli/commands/push.js +249 -87
- package/dist/cli/commands/push.js.map +1 -1
- package/dist/cli/commands/secret.d.ts.map +1 -1
- package/dist/cli/commands/secret.js +18 -3
- package/dist/cli/commands/secret.js.map +1 -1
- package/dist/cli/commands/signup.js.map +1 -1
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +8 -3
- package/dist/cli/commands/verify.js.map +1 -1
- package/dist/cli/commands/whoami.d.ts.map +1 -1
- package/dist/cli/commands/whoami.js +11 -2
- package/dist/cli/commands/whoami.js.map +1 -1
- package/dist/cli/commands/workflow.d.ts +1 -1
- package/dist/cli/commands/workflow.d.ts.map +1 -1
- package/dist/cli/commands/workflow.js +2 -2
- package/dist/cli/commands/workflow.js.map +1 -1
- package/dist/cli/deploy/bundler.d.ts +5 -0
- package/dist/cli/deploy/bundler.d.ts.map +1 -1
- package/dist/cli/deploy/bundler.js +29 -0
- package/dist/cli/deploy/bundler.js.map +1 -1
- package/dist/cli/index.d.ts +51 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +71 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/cli-io-impl.d.ts.map +1 -1
- package/dist/cli/utils/cli-io-impl.js +1 -1
- package/dist/cli/utils/cli-io-impl.js.map +1 -1
- package/dist/cli/utils/cli-io.d.ts +15 -5
- package/dist/cli/utils/cli-io.d.ts.map +1 -1
- package/dist/cli/utils/config.d.ts +22 -0
- package/dist/cli/utils/config.d.ts.map +1 -1
- package/dist/cli/utils/config.js +26 -0
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/cli/utils/create-project.d.ts +58 -0
- package/dist/cli/utils/create-project.d.ts.map +1 -0
- package/dist/cli/utils/create-project.js +77 -0
- package/dist/cli/utils/create-project.js.map +1 -0
- package/dist/cli/utils/environment.d.ts +1 -1
- package/dist/cli/utils/environment.d.ts.map +1 -1
- package/dist/cli/utils/format.js.map +1 -1
- package/dist/cli/utils/node-version.js.map +1 -1
- package/dist/cli/utils/project-name.js.map +1 -1
- package/dist/cli/utils/prompt-new-project.d.ts +2 -2
- package/dist/cli/utils/prompt-new-project.d.ts.map +1 -1
- package/dist/cli/utils/prompt-new-project.js.map +1 -1
- package/dist/cli/utils/retry.d.ts +20 -2
- package/dist/cli/utils/retry.d.ts.map +1 -1
- package/dist/cli/utils/retry.js +37 -12
- package/dist/cli/utils/retry.js.map +1 -1
- package/dist/repo-shared/constants.d.ts.map +1 -1
- package/dist/repo-shared/cost.d.ts +13 -0
- package/dist/repo-shared/cost.d.ts.map +1 -1
- package/dist/repo-shared/cost.js.map +1 -1
- package/dist/repo-shared/deploy-protocol.d.ts +34 -0
- package/dist/repo-shared/deploy-protocol.d.ts.map +1 -0
- package/dist/repo-shared/deploy-protocol.js +168 -0
- package/dist/repo-shared/deploy-protocol.js.map +1 -0
- package/dist/repo-shared/package-metadata.js +1 -1
- package/dist/repo-shared/package-metadata.js.map +1 -1
- package/dist/repo-shared/slug-validation.d.ts.map +1 -1
- package/dist/repo-shared/slug-validation.js +7 -0
- package/dist/repo-shared/slug-validation.js.map +1 -1
- package/dist/runtime/auth.d.ts.map +1 -1
- package/dist/runtime/auth.js +2 -1
- package/dist/runtime/auth.js.map +1 -1
- package/dist/runtime/db.d.ts.map +1 -1
- package/dist/runtime/discord.d.ts.map +1 -1
- package/dist/runtime/email-send.d.ts.map +1 -1
- package/dist/runtime/entry.js.map +1 -1
- package/dist/runtime/log.d.ts.map +1 -1
- package/dist/runtime/log.js.map +1 -1
- package/dist/runtime/secrets.d.ts.map +1 -1
- package/dist/runtime/slack.d.ts.map +1 -1
- package/dist/runtime/storage.js +1 -1
- package/dist/runtime/storage.js.map +1 -1
- package/dist/shared/non-json-response.d.ts +90 -0
- package/dist/shared/non-json-response.d.ts.map +1 -0
- package/dist/shared/non-json-response.js +211 -0
- package/dist/shared/non-json-response.js.map +1 -0
- package/dist/worker-runtime/auth.d.ts.map +1 -1
- package/dist/worker-runtime/auth.js +59 -18
- package/dist/worker-runtime/auth.js.map +1 -1
- package/dist/worker-runtime/d1-event-store.d.ts.map +1 -1
- package/dist/worker-runtime/discord.d.ts.map +1 -1
- package/dist/worker-runtime/email-send.d.ts.map +1 -1
- package/dist/worker-runtime/entry-extract.d.ts +38 -6
- package/dist/worker-runtime/entry-extract.d.ts.map +1 -1
- package/dist/worker-runtime/entry-extract.js +30 -18
- package/dist/worker-runtime/entry-extract.js.map +1 -1
- package/dist/worker-runtime/entry.d.ts +1 -0
- package/dist/worker-runtime/entry.d.ts.map +1 -1
- package/dist/worker-runtime/entry.js +89 -1
- package/dist/worker-runtime/entry.js.map +1 -1
- package/dist/worker-runtime/index.d.ts.map +1 -1
- package/dist/worker-runtime/slack.d.ts.map +1 -1
- package/dist/worker-runtime/types.d.ts +2 -0
- package/dist/worker-runtime/types.d.ts.map +1 -1
- package/dist/workflow/runtime.d.ts +22 -1
- package/dist/workflow/runtime.d.ts.map +1 -1
- package/dist/workflow/runtime.js +25 -16
- package/dist/workflow/runtime.js.map +1 -1
- package/node_modules/better-sqlite3/deps/defines.gypi +1 -0
- package/node_modules/better-sqlite3/deps/download.sh +2 -1
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.c +7696 -4295
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.h +482 -103
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3ext.h +10 -1
- package/node_modules/better-sqlite3/package.json +3 -3
- package/node_modules/better-sqlite3/src/better_sqlite3.cpp +1 -1
- package/node_modules/better-sqlite3/src/util/helpers.cpp +1 -1
- package/node_modules/better-sqlite3/src/util/macros.cpp +8 -1
- package/node_modules/flingflow/biome.json +15 -0
- package/node_modules/flingflow/dist/backoff.js +1 -1
- package/node_modules/flingflow/dist/backoff.js.map +1 -1
- package/node_modules/flingflow/dist/engine.js +0 -1
- package/node_modules/flingflow/dist/engine.js.map +1 -1
- package/node_modules/flingflow/dist/store-memory.js +0 -1
- package/node_modules/flingflow/dist/store-memory.js.map +1 -1
- package/node_modules/flingflow/dist/store-sqlite.js +0 -1
- package/node_modules/flingflow/dist/store-sqlite.js.map +1 -1
- package/node_modules/flingflow/dist/stress.js +7 -8
- package/node_modules/flingflow/dist/stress.js.map +1 -1
- package/node_modules/flingflow/dist/transitions.js.map +1 -1
- package/node_modules/flingflow/package.json +7 -10
- package/node_modules/flingflow/src/backoff.ts +1 -1
- package/node_modules/flingflow/src/engine.ts +1 -2
- package/node_modules/flingflow/src/store-memory.ts +0 -1
- package/node_modules/flingflow/src/store-sqlite.ts +0 -1
- package/node_modules/flingflow/src/stress.ts +9 -10
- package/node_modules/flingflow/test/engine.test.ts +16 -15
- package/node_modules/flingflow/test/recovery.test.ts +5 -4
- package/node_modules/flingflow/test/simulation.test.ts +21 -20
- package/node_modules/flingflow/test/store-conformance.ts +25 -21
- package/node_modules/flingflow/tsconfig.typecheck.json +1 -1
- package/node_modules/semver/README.md +22 -6
- package/node_modules/semver/bin/semver.js +15 -11
- package/node_modules/semver/classes/range.js +24 -4
- package/node_modules/semver/classes/semver.js +19 -2
- package/node_modules/semver/functions/diff.js +1 -1
- package/node_modules/semver/functions/truncate.js +48 -0
- package/node_modules/semver/index.js +2 -0
- package/node_modules/semver/internal/re.js +3 -3
- package/node_modules/semver/package.json +4 -4
- package/node_modules/semver/range.bnf +5 -4
- package/node_modules/semver/ranges/subset.js +3 -3
- package/package.json +36 -32
- package/templates/default/dot-claude/skills/fling/.hash +1 -1
- package/templates/default/dot-claude/skills/fling/SKILL.md +0 -1
- package/templates/default/package.json +9 -9
- package/dist/cli/commands/tunnel.d.ts +0 -41
- package/dist/cli/commands/tunnel.d.ts.map +0 -1
- package/dist/cli/commands/tunnel.js +0 -211
- package/dist/cli/commands/tunnel.js.map +0 -1
- package/node_modules/flingflow/eslint.config.js +0 -94
- package/node_modules/flingflow/tsconfig.lint.json +0 -5
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defined } from "@glideapps/ts-necessities";
|
|
1
2
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
3
|
import { Engine, UnknownWorkflowError } from "../src/engine.js";
|
|
3
4
|
import { MemoryEventStore } from "../src/store-memory.js";
|
|
@@ -53,12 +54,12 @@ describe("Engine", () => {
|
|
|
53
54
|
|
|
54
55
|
const events = (await store.getAllEvents(runId, noSim))._unsafeUnwrap();
|
|
55
56
|
expect(events).toHaveLength(1);
|
|
56
|
-
expect(events[0].event_type).toBe("workflow_created");
|
|
57
|
-
expect(events[0].payload).toEqual({ workflowName: "single-step", writes: { value: "hello" }, config: { maxAttempts: 5, initialBackoffMS: 1000, backoffMultiplier: 2, maxBackoffMS: 60_000, stepTimeoutMS: 300_000 } });
|
|
58
|
-
expect(events[0].seq).toBe(0);
|
|
59
|
-
expect(events[0].created_at).toBe(1000);
|
|
60
|
-
expect(events[0].run_id).toBe("run-1");
|
|
61
|
-
expect(events[0].workflow_id).toBe("wf-single");
|
|
57
|
+
expect(defined(events[0]).event_type).toBe("workflow_created");
|
|
58
|
+
expect(defined(events[0]).payload).toEqual({ workflowName: "single-step", writes: { value: "hello" }, config: { maxAttempts: 5, initialBackoffMS: 1000, backoffMultiplier: 2, maxBackoffMS: 60_000, stepTimeoutMS: 300_000 } });
|
|
59
|
+
expect(defined(events[0]).seq).toBe(0);
|
|
60
|
+
expect(defined(events[0]).created_at).toBe(1000);
|
|
61
|
+
expect(defined(events[0]).run_id).toBe("run-1");
|
|
62
|
+
expect(defined(events[0]).workflow_id).toBe("wf-single");
|
|
62
63
|
});
|
|
63
64
|
|
|
64
65
|
it("rejects unknown workflow names", async () => {
|
|
@@ -109,7 +110,7 @@ describe("Engine", () => {
|
|
|
109
110
|
|
|
110
111
|
const events = (await store.getAllEvents(runId, noSim))._unsafeUnwrap();
|
|
111
112
|
const lastEvent = events[events.length - 1];
|
|
112
|
-
expect(lastEvent.event_type).toBe("workflow_failed");
|
|
113
|
+
expect(defined(lastEvent).event_type).toBe("workflow_failed");
|
|
113
114
|
});
|
|
114
115
|
});
|
|
115
116
|
|
|
@@ -180,8 +181,8 @@ describe("Engine", () => {
|
|
|
180
181
|
|
|
181
182
|
// Check attempt numbers
|
|
182
183
|
const starts = events.filter(e => e.event_type === "step_started");
|
|
183
|
-
expect((starts[0].payload as any).attempt).toBe(1);
|
|
184
|
-
expect((starts[1].payload as any).attempt).toBe(2);
|
|
184
|
+
expect((defined(starts[0]).payload as any).attempt).toBe(1);
|
|
185
|
+
expect((defined(starts[1]).payload as any).attempt).toBe(2);
|
|
185
186
|
});
|
|
186
187
|
|
|
187
188
|
it("max attempts exceeded → workflow_failed", async () => {
|
|
@@ -236,7 +237,7 @@ describe("Engine", () => {
|
|
|
236
237
|
|
|
237
238
|
const events = (await store.getAllEvents(runId, noSim))._unsafeUnwrap();
|
|
238
239
|
for (let i = 0; i < events.length; i++) {
|
|
239
|
-
expect(events[i].seq).toBe(i);
|
|
240
|
+
expect(defined(events[i]).seq).toBe(i);
|
|
240
241
|
}
|
|
241
242
|
});
|
|
242
243
|
|
|
@@ -267,7 +268,7 @@ describe("Engine", () => {
|
|
|
267
268
|
(await engine.runToCompletion(runId, noSim))._unsafeUnwrap();
|
|
268
269
|
|
|
269
270
|
const events = (await store.getAllEvents(runId, noSim))._unsafeUnwrap();
|
|
270
|
-
expect(events[0].created_at).toBe(5000); // workflow_created
|
|
271
|
+
expect(defined(events[0]).created_at).toBe(5000); // workflow_created
|
|
271
272
|
for (const event of events.slice(1)) {
|
|
272
273
|
expect(event.created_at).toBe(5100); // all others at clock=5100
|
|
273
274
|
}
|
|
@@ -286,8 +287,8 @@ describe("Engine", () => {
|
|
|
286
287
|
};
|
|
287
288
|
|
|
288
289
|
for (let i = 0; i < events.length - 1; i++) {
|
|
289
|
-
const from = events[i].event_type;
|
|
290
|
-
const to = events[i + 1].event_type;
|
|
290
|
+
const from = defined(events[i]).event_type;
|
|
291
|
+
const to = defined(events[i + 1]).event_type;
|
|
291
292
|
expect(
|
|
292
293
|
validTransitions[from],
|
|
293
294
|
`No valid transitions from ${from}`,
|
|
@@ -344,8 +345,8 @@ describe("Engine", () => {
|
|
|
344
345
|
|
|
345
346
|
const events1 = (await store.getAllEvents(runId1, noSim))._unsafeUnwrap();
|
|
346
347
|
const events2 = (await store.getAllEvents(runId2, noSim))._unsafeUnwrap();
|
|
347
|
-
expect(events1[events1.length - 1].event_type).toBe("workflow_completed");
|
|
348
|
-
expect(events2[events2.length - 1].event_type).toBe("workflow_completed");
|
|
348
|
+
expect(defined(events1[events1.length - 1]).event_type).toBe("workflow_completed");
|
|
349
|
+
expect(defined(events2[events2.length - 1]).event_type).toBe("workflow_completed");
|
|
349
350
|
});
|
|
350
351
|
|
|
351
352
|
it("successive runs of same workflow ID get different run IDs", async () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defined } from "@glideapps/ts-necessities";
|
|
1
2
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
3
|
import { runRecovery } from "../src/recovery.js";
|
|
3
4
|
import { Engine } from "../src/engine.js";
|
|
@@ -52,9 +53,9 @@ describe("recovery", () => {
|
|
|
52
53
|
// step_failed should have been appended
|
|
53
54
|
const events = (await store.getAllEvents("run-1", noSim))._unsafeUnwrap();
|
|
54
55
|
expect(events).toHaveLength(3);
|
|
55
|
-
expect(events[2].event_type).toBe("step_failed");
|
|
56
|
-
expect((events[2].payload as any).reason).toBe("timeout");
|
|
57
|
-
expect((events[2].payload as any).retryable).toBe(true);
|
|
56
|
+
expect(defined(events[2]).event_type).toBe("step_failed");
|
|
57
|
+
expect((defined(events[2]).payload as any).reason).toBe("timeout");
|
|
58
|
+
expect((defined(events[2]).payload as any).retryable).toBe(true);
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
it("detects stuck workflow at workflow_created", async () => {
|
|
@@ -138,7 +139,7 @@ describe("recovery", () => {
|
|
|
138
139
|
|
|
139
140
|
const events = (await store.getAllEvents("run-10", noSim))._unsafeUnwrap();
|
|
140
141
|
const last = events[events.length - 1];
|
|
141
|
-
expect(last.event_type).toBe("workflow_completed");
|
|
142
|
+
expect(defined(last).event_type).toBe("workflow_completed");
|
|
142
143
|
});
|
|
143
144
|
|
|
144
145
|
it("detects step_completed without next_step as needing workflow_completed", async () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defined } from "@glideapps/ts-necessities";
|
|
1
2
|
import { describe, it, expect } from "vitest";
|
|
2
3
|
import {
|
|
3
4
|
SimulationImpl,
|
|
@@ -71,24 +72,24 @@ function makeCheck(): EventStoreCheck {
|
|
|
71
72
|
|
|
72
73
|
// Ascending seq with no gaps
|
|
73
74
|
for (let i = 0; i < events.length; i++) {
|
|
74
|
-
if (events[i].seq !== i) {
|
|
75
|
+
if (defined(events[i]).seq !== i) {
|
|
75
76
|
throw new Error(
|
|
76
|
-
`Check failed [${runId}]: expected seq ${i}, got ${events[i].seq}`,
|
|
77
|
+
`Check failed [${runId}]: expected seq ${i}, got ${defined(events[i]).seq}`,
|
|
77
78
|
);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
// First event must be workflow_created
|
|
82
|
-
if (events[0].event_type !== "workflow_created") {
|
|
83
|
+
if (defined(events[0]).event_type !== "workflow_created") {
|
|
83
84
|
throw new Error(
|
|
84
|
-
`Check failed [${runId}]: first event is ${events[0].event_type}, expected workflow_created`,
|
|
85
|
+
`Check failed [${runId}]: first event is ${defined(events[0]).event_type}, expected workflow_created`,
|
|
85
86
|
);
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
// All transitions are valid
|
|
89
90
|
for (let i = 0; i < events.length - 1; i++) {
|
|
90
|
-
const from = events[i].event_type;
|
|
91
|
-
const to = events[i + 1].event_type;
|
|
91
|
+
const from = defined(events[i]).event_type;
|
|
92
|
+
const to = defined(events[i + 1]).event_type;
|
|
92
93
|
const allowed = validTransitions[from];
|
|
93
94
|
if (!allowed?.includes(to)) {
|
|
94
95
|
throw new Error(
|
|
@@ -114,15 +115,15 @@ function makeCheck(): EventStoreCheck {
|
|
|
114
115
|
function validateEventTrace(events: WorkflowEvent[]): void {
|
|
115
116
|
// Check ascending seq
|
|
116
117
|
for (let i = 0; i < events.length; i++) {
|
|
117
|
-
if (events[i].seq !== i) {
|
|
118
|
-
throw new Error(`Expected seq ${i}, got ${events[i].seq}`);
|
|
118
|
+
if (defined(events[i]).seq !== i) {
|
|
119
|
+
throw new Error(`Expected seq ${i}, got ${defined(events[i]).seq}`);
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
// Check valid transitions
|
|
123
124
|
for (let i = 0; i < events.length - 1; i++) {
|
|
124
|
-
const from = events[i].event_type;
|
|
125
|
-
const to = events[i + 1].event_type;
|
|
125
|
+
const from = defined(events[i]).event_type;
|
|
126
|
+
const to = defined(events[i + 1]).event_type;
|
|
126
127
|
const allowed = validTransitions[from];
|
|
127
128
|
if (!allowed?.includes(to)) {
|
|
128
129
|
throw new Error(
|
|
@@ -134,17 +135,17 @@ function validateEventTrace(events: WorkflowEvent[]): void {
|
|
|
134
135
|
// Check terminal
|
|
135
136
|
const last = events[events.length - 1];
|
|
136
137
|
if (
|
|
137
|
-
last.event_type !== "workflow_completed" &&
|
|
138
|
-
last.event_type !== "workflow_failed"
|
|
138
|
+
defined(last).event_type !== "workflow_completed" &&
|
|
139
|
+
defined(last).event_type !== "workflow_failed"
|
|
139
140
|
) {
|
|
140
|
-
throw new Error(`Workflow not terminal: last event is ${last.event_type}`);
|
|
141
|
+
throw new Error(`Workflow not terminal: last event is ${defined(last).event_type}`);
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
function simulationTests(
|
|
145
146
|
name: string,
|
|
146
147
|
createStore: () => Promise<EventStore>,
|
|
147
|
-
) {
|
|
148
|
+
): void {
|
|
148
149
|
describe(`simulation tests (${name})`, () => {
|
|
149
150
|
it("concurrent workers: exactly one claims each step", async () => {
|
|
150
151
|
const store = await createStore();
|
|
@@ -188,8 +189,8 @@ function simulationTests(
|
|
|
188
189
|
validateEventTrace(events);
|
|
189
190
|
|
|
190
191
|
const last = events[events.length - 1];
|
|
191
|
-
expect(last.event_type).toBe("workflow_completed");
|
|
192
|
-
expect((last.payload as any).result).toEqual({ finalCount: 3 });
|
|
192
|
+
expect(defined(last).event_type).toBe("workflow_completed");
|
|
193
|
+
expect((defined(last).payload as any).result).toEqual({ finalCount: 3 });
|
|
193
194
|
});
|
|
194
195
|
|
|
195
196
|
it("concurrent workers across multiple seeds", async () => {
|
|
@@ -242,7 +243,7 @@ function simulationTests(
|
|
|
242
243
|
validateEventTrace(events);
|
|
243
244
|
|
|
244
245
|
const last = events[events.length - 1];
|
|
245
|
-
expect(last.event_type).toBe("workflow_completed");
|
|
246
|
+
expect(defined(last).event_type).toBe("workflow_completed");
|
|
246
247
|
}
|
|
247
248
|
});
|
|
248
249
|
|
|
@@ -315,12 +316,12 @@ function simulationTests(
|
|
|
315
316
|
|
|
316
317
|
if (events.length > 0) {
|
|
317
318
|
for (let i = 0; i < events.length; i++) {
|
|
318
|
-
expect(events[i].seq).toBe(i);
|
|
319
|
+
expect(defined(events[i]).seq).toBe(i);
|
|
319
320
|
}
|
|
320
321
|
|
|
321
322
|
for (let i = 0; i < events.length - 1; i++) {
|
|
322
|
-
const from = events[i].event_type;
|
|
323
|
-
const to = events[i + 1].event_type;
|
|
323
|
+
const from = defined(events[i]).event_type;
|
|
324
|
+
const to = defined(events[i + 1]).event_type;
|
|
324
325
|
const allowed = validTransitions[from];
|
|
325
326
|
expect(allowed, `No transitions defined from ${from}`).toBeDefined();
|
|
326
327
|
expect(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defined } from "@glideapps/ts-necessities";
|
|
1
2
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
3
|
import { UniqueConstraintError, WorkflowAlreadyActiveError, WorkflowIdMismatchError, type EventStore } from "../src/store.js";
|
|
3
4
|
import type { WorkflowConfig, WorkflowEvent } from "../src/types.js";
|
|
@@ -5,7 +6,7 @@ import { noSim } from "./fixtures.js";
|
|
|
5
6
|
|
|
6
7
|
const TEST_CONFIG: WorkflowConfig = { maxAttempts: 5, initialBackoffMS: 1000, backoffMultiplier: 2, maxBackoffMS: 60_000, stepTimeoutMS: 300_000 };
|
|
7
8
|
|
|
8
|
-
export function storeConformanceTests(name: string, createStore: () => Promise<EventStore>) {
|
|
9
|
+
export function storeConformanceTests(name: string, createStore: () => Promise<EventStore>): void {
|
|
9
10
|
describe(`${name} conformance`, () => {
|
|
10
11
|
let store: EventStore;
|
|
11
12
|
|
|
@@ -43,8 +44,8 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
43
44
|
}, noSim))._unsafeUnwrap();
|
|
44
45
|
|
|
45
46
|
const last = (await store.getLastEvent("run-1", noSim))._unsafeUnwrap();
|
|
46
|
-
expect(last
|
|
47
|
-
expect(last
|
|
47
|
+
expect(last?.seq).toBe(1);
|
|
48
|
+
expect(last?.event_type).toBe("step_started");
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
it("returns UniqueConstraintError on duplicate (run_id, seq)", async () => {
|
|
@@ -88,7 +89,7 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
88
89
|
// seq=1 should NOT be present since batch is atomic
|
|
89
90
|
const all = (await store.getAllEvents("run-1", noSim))._unsafeUnwrap();
|
|
90
91
|
expect(all).toHaveLength(1);
|
|
91
|
-
expect(all[0].seq).toBe(0);
|
|
92
|
+
expect(defined(all[0]).seq).toBe(0);
|
|
92
93
|
});
|
|
93
94
|
|
|
94
95
|
it("getEvent returns specific event", async () => {
|
|
@@ -98,9 +99,12 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
98
99
|
}, noSim))._unsafeUnwrap();
|
|
99
100
|
|
|
100
101
|
const event = (await store.getEvent("run-1", 0, noSim))._unsafeUnwrap();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
// `defined()` only rules out undefined, and getEvent returns
|
|
103
|
+
// `WorkflowEvent | null` — narrow explicitly so the assertions below run
|
|
104
|
+
// against a real event rather than silently against null.
|
|
105
|
+
if (event === null) throw new Error("getEvent returned null for seq 0");
|
|
106
|
+
expect(event.event_type).toBe("workflow_created");
|
|
107
|
+
expect((event.payload as any).writes).toEqual({ x: 1 });
|
|
104
108
|
});
|
|
105
109
|
|
|
106
110
|
it("getEvent returns null for missing seq", async () => {
|
|
@@ -119,8 +123,8 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
119
123
|
|
|
120
124
|
const completed = (await store.getCompletedEvents("run-1", noSim))._unsafeUnwrap();
|
|
121
125
|
expect(completed).toHaveLength(2);
|
|
122
|
-
expect(completed[0].seq).toBe(4); // newest first
|
|
123
|
-
expect(completed[1].seq).toBe(2);
|
|
126
|
+
expect(defined(completed[0]).seq).toBe(4); // newest first
|
|
127
|
+
expect(defined(completed[1]).seq).toBe(2);
|
|
124
128
|
});
|
|
125
129
|
|
|
126
130
|
it("getAllEvents returns events in seq ASC order", async () => {
|
|
@@ -131,8 +135,8 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
131
135
|
|
|
132
136
|
const all = (await store.getAllEvents("run-1", noSim))._unsafeUnwrap();
|
|
133
137
|
expect(all).toHaveLength(2);
|
|
134
|
-
expect(all[0].seq).toBe(0);
|
|
135
|
-
expect(all[1].seq).toBe(1);
|
|
138
|
+
expect(defined(all[0]).seq).toBe(0);
|
|
139
|
+
expect(defined(all[1]).seq).toBe(1);
|
|
136
140
|
});
|
|
137
141
|
|
|
138
142
|
it("getActiveWorkflows excludes terminal workflows", async () => {
|
|
@@ -160,7 +164,7 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
160
164
|
|
|
161
165
|
const active = (await store.getActiveWorkflows(noSim))._unsafeUnwrap();
|
|
162
166
|
expect(active).toHaveLength(1);
|
|
163
|
-
expect(active[0].run_id).toBe("run-active");
|
|
167
|
+
expect(defined(active[0]).run_id).toBe("run-active");
|
|
164
168
|
});
|
|
165
169
|
|
|
166
170
|
it("JSON round-trips payloads correctly", async () => {
|
|
@@ -175,8 +179,8 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
175
179
|
}, noSim))._unsafeUnwrap();
|
|
176
180
|
|
|
177
181
|
const retrieved = (await store.getEvent("run-json", 0, noSim))._unsafeUnwrap();
|
|
178
|
-
expect(retrieved
|
|
179
|
-
expect(retrieved
|
|
182
|
+
expect(retrieved?.payload).toEqual(payload);
|
|
183
|
+
expect(retrieved?.payload).not.toBe(payload);
|
|
180
184
|
});
|
|
181
185
|
|
|
182
186
|
it("isolates events between different runs", async () => {
|
|
@@ -193,8 +197,8 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
193
197
|
const allB = (await store.getAllEvents("run-b", noSim))._unsafeUnwrap();
|
|
194
198
|
expect(allA).toHaveLength(1);
|
|
195
199
|
expect(allB).toHaveLength(1);
|
|
196
|
-
expect((allA[0].payload as any).workflowName).toBe("a");
|
|
197
|
-
expect((allB[0].payload as any).workflowName).toBe("b");
|
|
200
|
+
expect((defined(allA[0]).payload as any).workflowName).toBe("a");
|
|
201
|
+
expect((defined(allB[0]).payload as any).workflowName).toBe("b");
|
|
198
202
|
});
|
|
199
203
|
|
|
200
204
|
// --- appendIfNoActiveWorkflow tests ---
|
|
@@ -268,9 +272,9 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
268
272
|
|
|
269
273
|
const runs = (await store.getRecentRuns(10, noSim))._unsafeUnwrap();
|
|
270
274
|
expect(runs).toHaveLength(3);
|
|
271
|
-
expect(runs[0].run_id).toBe("run-new");
|
|
272
|
-
expect(runs[1].run_id).toBe("run-mid");
|
|
273
|
-
expect(runs[2].run_id).toBe("run-old");
|
|
275
|
+
expect(defined(runs[0]).run_id).toBe("run-new");
|
|
276
|
+
expect(defined(runs[1]).run_id).toBe("run-mid");
|
|
277
|
+
expect(defined(runs[2]).run_id).toBe("run-old");
|
|
274
278
|
// All should be seq=0 (workflow_created)
|
|
275
279
|
for (const run of runs) {
|
|
276
280
|
expect(run.seq).toBe(0);
|
|
@@ -294,8 +298,8 @@ export function storeConformanceTests(name: string, createStore: () => Promise<E
|
|
|
294
298
|
|
|
295
299
|
const runs = (await store.getRecentRuns(2, noSim))._unsafeUnwrap();
|
|
296
300
|
expect(runs).toHaveLength(2);
|
|
297
|
-
expect(runs[0].run_id).toBe("run-3");
|
|
298
|
-
expect(runs[1].run_id).toBe("run-2");
|
|
301
|
+
expect(defined(runs[0]).run_id).toBe("run-3");
|
|
302
|
+
expect(defined(runs[1]).run_id).toBe("run-2");
|
|
299
303
|
});
|
|
300
304
|
|
|
301
305
|
it("getRecentRuns returns empty array when no runs exist", async () => {
|
|
@@ -56,6 +56,7 @@ const semverCompareLoose = require('semver/functions/compare-loose')
|
|
|
56
56
|
const semverCompareBuild = require('semver/functions/compare-build')
|
|
57
57
|
const semverSort = require('semver/functions/sort')
|
|
58
58
|
const semverRsort = require('semver/functions/rsort')
|
|
59
|
+
const semverTruncate = require('semver/functions/truncate')
|
|
59
60
|
|
|
60
61
|
// low-level comparators between versions
|
|
61
62
|
const semverGt = require('semver/functions/gt')
|
|
@@ -110,8 +111,9 @@ Options:
|
|
|
110
111
|
-l --loose
|
|
111
112
|
Interpret versions and ranges loosely
|
|
112
113
|
|
|
113
|
-
-n <0|1>
|
|
114
|
-
|
|
114
|
+
-n <0|1|false>
|
|
115
|
+
Base number for prerelease identifier (default: 0).
|
|
116
|
+
Use false to omit the number altogether.
|
|
115
117
|
|
|
116
118
|
-p --include-prerelease
|
|
117
119
|
Always include prerelease versions in range matching
|
|
@@ -398,12 +400,19 @@ nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
|
|
|
398
400
|
tilde ::= '~' partial
|
|
399
401
|
caret ::= '^' partial
|
|
400
402
|
qualifier ::= ( '-' pre )? ( '+' build )?
|
|
401
|
-
pre ::=
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
403
|
+
pre ::= prepart ( '.' prepart ) *
|
|
404
|
+
prepart ::= nr | alphanumid
|
|
405
|
+
build ::= buildid ( '.' buildid ) *
|
|
406
|
+
alphanumid ::= ( ['0'-'9'] ) * [-A-Za-z] [-0-9A-Za-z] *
|
|
407
|
+
buildid ::= [-0-9A-Za-z]+
|
|
405
408
|
```
|
|
406
409
|
|
|
410
|
+
Note: Prerelease identifiers (`pre`) use `nr` for numeric parts, which
|
|
411
|
+
disallows leading zeros (e.g., `1.2.3-00` is invalid). Build metadata
|
|
412
|
+
identifiers (`build`) allow any alphanumeric string including leading
|
|
413
|
+
zeros (e.g., `1.2.3+00` is valid). This matches the
|
|
414
|
+
[SemVer 2.0.0 specification](https://semver.org/#spec-item-9).
|
|
415
|
+
|
|
407
416
|
## Functions
|
|
408
417
|
|
|
409
418
|
All methods and classes take a final `options` object argument. All
|
|
@@ -448,6 +457,12 @@ strings that they parse.
|
|
|
448
457
|
or comparators intersect.
|
|
449
458
|
* `parse(v)`: Attempt to parse a string as a semantic version, returning either
|
|
450
459
|
a `SemVer` object or `null`.
|
|
460
|
+
* `truncate(v, releaseType)`: Return the version with components _lower_
|
|
461
|
+
than `releaseType` dropped off, e.g.:
|
|
462
|
+
* `major` removes build & prerelease info and sets minor & patch to 0.
|
|
463
|
+
* `minor` removes build & prerelease info, and sets patch to 0
|
|
464
|
+
* `patch` removes build & prerelease info
|
|
465
|
+
* All prerelease types remove build info only
|
|
451
466
|
|
|
452
467
|
### Comparison
|
|
453
468
|
|
|
@@ -649,6 +664,7 @@ The following modules are available:
|
|
|
649
664
|
* `require('semver/functions/rsort')`
|
|
650
665
|
* `require('semver/functions/satisfies')`
|
|
651
666
|
* `require('semver/functions/sort')`
|
|
667
|
+
* `require('semver/functions/truncate')`
|
|
652
668
|
* `require('semver/functions/valid')`
|
|
653
669
|
* `require('semver/ranges/gtr')`
|
|
654
670
|
* `require('semver/ranges/intersects')`
|
|
@@ -46,6 +46,7 @@ const main = () => {
|
|
|
46
46
|
a = a.slice(0, indexOfEqualSign)
|
|
47
47
|
argv.unshift(value)
|
|
48
48
|
}
|
|
49
|
+
|
|
49
50
|
switch (a) {
|
|
50
51
|
case '-rv': case '-rev': case '--rev': case '--reverse':
|
|
51
52
|
reverse = true
|
|
@@ -60,15 +61,10 @@ const main = () => {
|
|
|
60
61
|
versions.push(argv.shift())
|
|
61
62
|
break
|
|
62
63
|
case '-i': case '--inc': case '--increment':
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
inc = argv.shift()
|
|
68
|
-
break
|
|
69
|
-
default:
|
|
70
|
-
inc = 'patch'
|
|
71
|
-
break
|
|
64
|
+
if (semver.RELEASE_TYPES.includes(argv[0]) || (argv[0] === 'release')) {
|
|
65
|
+
inc = { value: argv.shift(), maybeErrantValue: null, option: a }
|
|
66
|
+
} else {
|
|
67
|
+
inc = { value: 'patch', maybeErrantValue: argv[0], option: a }
|
|
72
68
|
}
|
|
73
69
|
break
|
|
74
70
|
case '--preid':
|
|
@@ -102,10 +98,18 @@ const main = () => {
|
|
|
102
98
|
|
|
103
99
|
options = parseOptions({ loose, includePrerelease, rtl })
|
|
104
100
|
|
|
101
|
+
if (
|
|
102
|
+
inc &&
|
|
103
|
+
versions.includes(inc.maybeErrantValue) &&
|
|
104
|
+
!semver.valid(inc.maybeErrantValue, options)
|
|
105
|
+
) {
|
|
106
|
+
console.warn(`Invalid value for ${inc.option}; defaulting to 'patch'. This may become a failure in future major versions.`)
|
|
107
|
+
}
|
|
108
|
+
|
|
105
109
|
versions = versions.map((v) => {
|
|
106
110
|
return coerce ? (semver.coerce(v, options) || { version: v }).version : v
|
|
107
111
|
}).filter((v) => {
|
|
108
|
-
return semver.valid(v)
|
|
112
|
+
return semver.valid(v, options)
|
|
109
113
|
})
|
|
110
114
|
if (!versions.length) {
|
|
111
115
|
return fail()
|
|
@@ -125,7 +129,7 @@ const main = () => {
|
|
|
125
129
|
versions
|
|
126
130
|
.sort((a, b) => semver[reverse ? 'rcompare' : 'compare'](a, b, options))
|
|
127
131
|
.map(v => semver.clean(v, options))
|
|
128
|
-
.map(v => inc ? semver.inc(v, inc, options, identifier, identifierBase) : v)
|
|
132
|
+
.map(v => inc ? semver.inc(v, inc.value, options, identifier, identifierBase) : v)
|
|
129
133
|
.forEach(v => console.log(v))
|
|
130
134
|
}
|
|
131
135
|
|
|
@@ -98,6 +98,9 @@ class Range {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
parseRange (range) {
|
|
101
|
+
// strip build metadata so it can't bleed into the version
|
|
102
|
+
range = range.replace(BUILDSTRIPRE, '')
|
|
103
|
+
|
|
101
104
|
// memoize range parsing for performance.
|
|
102
105
|
// this is a very hot path, and fully deterministic.
|
|
103
106
|
const memoOpts =
|
|
@@ -223,6 +226,7 @@ const debug = require('../internal/debug')
|
|
|
223
226
|
const SemVer = require('./semver')
|
|
224
227
|
const {
|
|
225
228
|
safeRe: re,
|
|
229
|
+
src,
|
|
226
230
|
t,
|
|
227
231
|
comparatorTrimReplace,
|
|
228
232
|
tildeTrimReplace,
|
|
@@ -230,6 +234,9 @@ const {
|
|
|
230
234
|
} = require('../internal/re')
|
|
231
235
|
const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require('../internal/constants')
|
|
232
236
|
|
|
237
|
+
// unbounded global build-metadata stripper used by parseRange
|
|
238
|
+
const BUILDSTRIPRE = new RegExp(src[t.BUILD], 'g')
|
|
239
|
+
|
|
233
240
|
const isNullSet = c => c.value === '<0.0.0-0'
|
|
234
241
|
const isAny = c => c.value === ''
|
|
235
242
|
|
|
@@ -270,6 +277,11 @@ const parseComparator = (comp, options) => {
|
|
|
270
277
|
|
|
271
278
|
const isX = id => !id || id.toLowerCase() === 'x' || id === '*'
|
|
272
279
|
|
|
280
|
+
const invalidXRangeOrder = (M, m, p) => (
|
|
281
|
+
(isX(M) && !isX(m)) ||
|
|
282
|
+
(isX(m) && p && !isX(p))
|
|
283
|
+
)
|
|
284
|
+
|
|
273
285
|
// ~, ~> --> * (any, kinda silly)
|
|
274
286
|
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0
|
|
275
287
|
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0
|
|
@@ -287,6 +299,10 @@ const replaceTildes = (comp, options) => {
|
|
|
287
299
|
|
|
288
300
|
const replaceTilde = (comp, options) => {
|
|
289
301
|
const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]
|
|
302
|
+
// if we're including prereleases in the match, then the lower bound is
|
|
303
|
+
// -0, the lowest possible prerelease value, just like x-ranges and carets.
|
|
304
|
+
// this keeps `~1.2` equivalent to the `1.2.x` x-range it's documented as.
|
|
305
|
+
const z = options.includePrerelease ? '-0' : ''
|
|
290
306
|
return comp.replace(r, (_, M, m, p, pr) => {
|
|
291
307
|
debug('tilde', comp, _, M, m, p, pr)
|
|
292
308
|
let ret
|
|
@@ -294,10 +310,10 @@ const replaceTilde = (comp, options) => {
|
|
|
294
310
|
if (isX(M)) {
|
|
295
311
|
ret = ''
|
|
296
312
|
} else if (isX(m)) {
|
|
297
|
-
ret = `>=${M}.0.0 <${+M + 1}.0.0-0`
|
|
313
|
+
ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`
|
|
298
314
|
} else if (isX(p)) {
|
|
299
315
|
// ~1.2 == >=1.2.0 <1.3.0-0
|
|
300
|
-
ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`
|
|
316
|
+
ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`
|
|
301
317
|
} else if (pr) {
|
|
302
318
|
debug('replaceTilde pr', pr)
|
|
303
319
|
ret = `>=${M}.${m}.${p}-${pr
|
|
@@ -366,10 +382,10 @@ const replaceCaret = (comp, options) => {
|
|
|
366
382
|
if (M === '0') {
|
|
367
383
|
if (m === '0') {
|
|
368
384
|
ret = `>=${M}.${m}.${p
|
|
369
|
-
}
|
|
385
|
+
} <${M}.${m}.${+p + 1}-0`
|
|
370
386
|
} else {
|
|
371
387
|
ret = `>=${M}.${m}.${p
|
|
372
|
-
}
|
|
388
|
+
} <${M}.${+m + 1}.0-0`
|
|
373
389
|
}
|
|
374
390
|
} else {
|
|
375
391
|
ret = `>=${M}.${m}.${p
|
|
@@ -395,6 +411,10 @@ const replaceXRange = (comp, options) => {
|
|
|
395
411
|
const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]
|
|
396
412
|
return comp.replace(r, (ret, gtlt, M, m, p, pr) => {
|
|
397
413
|
debug('xRange', comp, ret, gtlt, M, m, p, pr)
|
|
414
|
+
if (invalidXRangeOrder(M, m, p)) {
|
|
415
|
+
return comp
|
|
416
|
+
}
|
|
417
|
+
|
|
398
418
|
const xM = isX(M)
|
|
399
419
|
const xm = xM || isX(m)
|
|
400
420
|
const xp = xm || isX(p)
|
|
@@ -6,6 +6,22 @@ const { safeRe: re, t } = require('../internal/re')
|
|
|
6
6
|
|
|
7
7
|
const parseOptions = require('../internal/parse-options')
|
|
8
8
|
const { compareIdentifiers } = require('../internal/identifiers')
|
|
9
|
+
|
|
10
|
+
const isPrereleaseIdentifier = (prerelease, identifier) => {
|
|
11
|
+
const identifiers = identifier.split('.')
|
|
12
|
+
if (identifiers.length > prerelease.length) {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < identifiers.length; i++) {
|
|
17
|
+
if (compareIdentifiers(prerelease[i], identifiers[i]) !== 0) {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
class SemVer {
|
|
10
26
|
constructor (version, options) {
|
|
11
27
|
options = parseOptions(options)
|
|
@@ -309,8 +325,9 @@ class SemVer {
|
|
|
309
325
|
if (identifierBase === false) {
|
|
310
326
|
prerelease = [identifier]
|
|
311
327
|
}
|
|
312
|
-
if (
|
|
313
|
-
|
|
328
|
+
if (isPrereleaseIdentifier(this.prerelease, identifier)) {
|
|
329
|
+
const prereleaseBase = this.prerelease[identifier.split('.').length]
|
|
330
|
+
if (isNaN(prereleaseBase)) {
|
|
314
331
|
this.prerelease = prerelease
|
|
315
332
|
}
|
|
316
333
|
} else {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const parse = require('./parse')
|
|
4
|
+
const constants = require('../internal/constants')
|
|
5
|
+
const SemVer = require('../classes/semver')
|
|
6
|
+
|
|
7
|
+
const truncate = (version, truncation, options) => {
|
|
8
|
+
if (!constants.RELEASE_TYPES.includes(truncation)) {
|
|
9
|
+
return null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const clonedVersion = cloneInputVersion(version, options)
|
|
13
|
+
return clonedVersion && doTruncation(clonedVersion, truncation)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const cloneInputVersion = (version, options) => {
|
|
17
|
+
const versionStringToParse = (
|
|
18
|
+
version instanceof SemVer ? version.version : version
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
return parse(versionStringToParse, options)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const doTruncation = (version, truncation) => {
|
|
25
|
+
if (isPrerelease(truncation)) {
|
|
26
|
+
return version.version
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
version.prerelease = []
|
|
30
|
+
|
|
31
|
+
switch (truncation) {
|
|
32
|
+
case 'major':
|
|
33
|
+
version.minor = 0
|
|
34
|
+
version.patch = 0
|
|
35
|
+
break
|
|
36
|
+
case 'minor':
|
|
37
|
+
version.patch = 0
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return version.format()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const isPrerelease = (type) => {
|
|
45
|
+
return type.startsWith('pre')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = truncate
|