@launchfile/macos-dev 0.2.0 → 0.4.0
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/bootstrap.d.ts +56 -8
- package/dist/bootstrap.js +170 -93
- package/dist/env-writer.d.ts +40 -11
- package/dist/env-writer.js +84 -23
- package/dist/health.d.ts +8 -2
- package/dist/health.js +11 -17
- package/dist/prereqs.js +2 -2
- package/dist/process-manager.d.ts +13 -0
- package/dist/process-manager.js +59 -3
- package/dist/process-stopper.d.ts +99 -0
- package/dist/process-stopper.js +165 -0
- package/dist/provider.d.ts +50 -0
- package/dist/provider.js +337 -28
- package/dist/redact.d.ts +55 -0
- package/dist/redact.js +92 -0
- package/dist/resources/identifiers.d.ts +22 -0
- package/dist/resources/identifiers.js +31 -0
- package/dist/resources/index.d.ts +3 -3
- package/dist/resources/index.js +21 -10
- package/dist/resources/mysql.d.ts +3 -1
- package/dist/resources/mysql.js +49 -11
- package/dist/resources/postgres.d.ts +4 -1
- package/dist/resources/postgres.js +63 -17
- package/dist/resources/redis.d.ts +3 -1
- package/dist/resources/redis.js +14 -4
- package/dist/resources/sqlite.js +2 -2
- package/dist/resources/types.d.ts +21 -3
- package/dist/runtimes/bun.js +2 -2
- package/dist/runtimes/installed-versions.d.ts +12 -0
- package/dist/runtimes/installed-versions.js +21 -0
- package/dist/runtimes/node.js +13 -8
- package/dist/runtimes/python.js +14 -7
- package/dist/runtimes/ruby.js +13 -8
- package/dist/secret-generator.js +11 -3
- package/dist/shell.d.ts +28 -6
- package/dist/shell.js +83 -28
- package/dist/state.d.ts +38 -0
- package/dist/state.js +12 -1
- package/dist/storage.d.ts +16 -3
- package/dist/storage.js +23 -6
- package/package.json +7 -4
- package/dist/__tests__/bootstrap.test.d.ts +0 -2
- package/dist/__tests__/bootstrap.test.js +0 -90
- package/dist/__tests__/dry-run.test.d.ts +0 -2
- package/dist/__tests__/dry-run.test.js +0 -141
- package/dist/__tests__/env-writer.test.d.ts +0 -2
- package/dist/__tests__/env-writer.test.js +0 -201
- package/dist/__tests__/lockfile-detect.test.d.ts +0 -2
- package/dist/__tests__/lockfile-detect.test.js +0 -75
- package/dist/__tests__/port-allocator.test.d.ts +0 -2
- package/dist/__tests__/port-allocator.test.js +0 -53
- package/dist/__tests__/secret-generator.test.d.ts +0 -2
- package/dist/__tests__/secret-generator.test.js +0 -26
- package/dist/__tests__/state.test.d.ts +0 -2
- package/dist/__tests__/state.test.js +0 -30
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { buildResolverContext, computeAppProperties, resolveComponentEnv, generateSecrets, } from "../env-writer.js";
|
|
3
|
-
const NO_APP = {};
|
|
4
|
-
describe("buildResolverContext", () => {
|
|
5
|
-
it("builds context from resources, ports, secrets, and app properties", () => {
|
|
6
|
-
const resources = {
|
|
7
|
-
postgres: {
|
|
8
|
-
url: "postgresql://user:pass@localhost:5432/mydb",
|
|
9
|
-
host: "localhost",
|
|
10
|
-
port: 5432,
|
|
11
|
-
user: "user",
|
|
12
|
-
password: "pass",
|
|
13
|
-
name: "mydb",
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
const ports = { backend: 3000, frontend: 3001 };
|
|
17
|
-
const secrets = { "jwt-secret": "abc123" };
|
|
18
|
-
const app = { name: "my-app", host: "localhost", port: 3000, url: "http://localhost:3000" };
|
|
19
|
-
const ctx = buildResolverContext(resources, ports, secrets, app);
|
|
20
|
-
expect(ctx.components?.backend).toEqual({
|
|
21
|
-
url: "http://localhost:3000",
|
|
22
|
-
host: "localhost",
|
|
23
|
-
port: 3000,
|
|
24
|
-
});
|
|
25
|
-
expect(ctx.resources?.postgres?.url).toBe("postgresql://user:pass@localhost:5432/mydb");
|
|
26
|
-
expect(ctx.secrets?.["jwt-secret"]).toBe("abc123");
|
|
27
|
-
expect(ctx.app?.url).toBe("http://localhost:3000");
|
|
28
|
-
expect(ctx.app?.name).toBe("my-app");
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe("computeAppProperties (D-33)", () => {
|
|
32
|
-
const baseLaunch = (overrides) => ({
|
|
33
|
-
name: "my-app",
|
|
34
|
-
components: {},
|
|
35
|
-
...overrides,
|
|
36
|
-
});
|
|
37
|
-
it("uses the first exposed component's port for $app.port and $app.url", () => {
|
|
38
|
-
const launch = baseLaunch({
|
|
39
|
-
components: {
|
|
40
|
-
default: {
|
|
41
|
-
provides: [{ protocol: "http", port: 3000, exposed: true }],
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
const app = computeAppProperties(launch, { default: 10042 });
|
|
46
|
-
expect(app.name).toBe("my-app");
|
|
47
|
-
expect(app.host).toBe("localhost");
|
|
48
|
-
expect(app.port).toBe(10042);
|
|
49
|
-
expect(app.url).toBe("http://localhost:10042");
|
|
50
|
-
});
|
|
51
|
-
it("picks the first exposed component in declaration order for multi-component apps", () => {
|
|
52
|
-
const launch = baseLaunch({
|
|
53
|
-
components: {
|
|
54
|
-
api: {
|
|
55
|
-
provides: [{ protocol: "http", port: 4000, exposed: true }],
|
|
56
|
-
},
|
|
57
|
-
worker: {
|
|
58
|
-
provides: [],
|
|
59
|
-
},
|
|
60
|
-
ui: {
|
|
61
|
-
provides: [{ protocol: "http", port: 5000, exposed: true }],
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
const app = computeAppProperties(launch, { api: 10043, ui: 10044 });
|
|
66
|
-
// Picks "api" because it's first in declaration order with exposed: true.
|
|
67
|
-
expect(app.port).toBe(10043);
|
|
68
|
-
expect(app.url).toBe("http://localhost:10043");
|
|
69
|
-
});
|
|
70
|
-
it("returns port 0 and empty url when no component is exposed", () => {
|
|
71
|
-
const launch = baseLaunch({
|
|
72
|
-
components: {
|
|
73
|
-
worker: {
|
|
74
|
-
provides: [],
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
const app = computeAppProperties(launch, {});
|
|
79
|
-
expect(app.port).toBe(0);
|
|
80
|
-
expect(app.url).toBe("");
|
|
81
|
-
expect(app.name).toBe("my-app");
|
|
82
|
-
});
|
|
83
|
-
// End-to-end: a component env var that uses $app.url resolves to the
|
|
84
|
-
// computed app URL. This is the contract that makes Firefly III's
|
|
85
|
-
// `APP_URL: default: $app.url` actually work end-to-end on macos-dev.
|
|
86
|
-
it("$app.url in component env defaults resolves to the computed URL", () => {
|
|
87
|
-
const launch = baseLaunch({
|
|
88
|
-
components: {
|
|
89
|
-
default: {
|
|
90
|
-
provides: [{ protocol: "http", port: 3000, exposed: true }],
|
|
91
|
-
env: { PUBLIC_URL: { default: "$app.url" } },
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
const app = computeAppProperties(launch, { default: 10042 });
|
|
96
|
-
const ctx = buildResolverContext({}, { default: 10042 }, {}, app);
|
|
97
|
-
const env = resolveComponentEnv(launch.components.default, ctx, {});
|
|
98
|
-
expect(env.PUBLIC_URL).toBe("http://localhost:10042");
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
describe("resolveComponentEnv", () => {
|
|
102
|
-
it("resolves set_env from requires", () => {
|
|
103
|
-
const component = {
|
|
104
|
-
requires: [
|
|
105
|
-
{
|
|
106
|
-
type: "postgres",
|
|
107
|
-
set_env: {
|
|
108
|
-
DATABASE_URL: "$url",
|
|
109
|
-
DB_HOST: "$host",
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
};
|
|
114
|
-
const resourceMap = {
|
|
115
|
-
postgres: {
|
|
116
|
-
url: "postgresql://user:pass@localhost:5432/mydb",
|
|
117
|
-
host: "localhost",
|
|
118
|
-
port: 5432,
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
const context = buildResolverContext(resourceMap, {}, {}, NO_APP);
|
|
122
|
-
const env = resolveComponentEnv(component, context, resourceMap);
|
|
123
|
-
expect(env.DATABASE_URL).toBe("postgresql://user:pass@localhost:5432/mydb");
|
|
124
|
-
expect(env.DB_HOST).toBe("localhost");
|
|
125
|
-
});
|
|
126
|
-
it("resolves component-level env defaults", () => {
|
|
127
|
-
const component = {
|
|
128
|
-
env: {
|
|
129
|
-
PORT: { default: "8080" },
|
|
130
|
-
LOG_LEVEL: { default: "info" },
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
const context = buildResolverContext({}, {}, {}, NO_APP);
|
|
134
|
-
const env = resolveComponentEnv(component, context, {});
|
|
135
|
-
expect(env.PORT).toBe("8080");
|
|
136
|
-
expect(env.LOG_LEVEL).toBe("info");
|
|
137
|
-
});
|
|
138
|
-
it("resolves cross-component references", () => {
|
|
139
|
-
const component = {
|
|
140
|
-
env: {
|
|
141
|
-
BACKEND_URL: { default: "$components.backend.url" },
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
const context = buildResolverContext({}, { backend: 3000 }, {}, NO_APP);
|
|
145
|
-
const env = resolveComponentEnv(component, context, {});
|
|
146
|
-
expect(env.BACKEND_URL).toBe("http://localhost:3000");
|
|
147
|
-
});
|
|
148
|
-
it("resolves secrets references", () => {
|
|
149
|
-
const component = {
|
|
150
|
-
env: {
|
|
151
|
-
SECRET_KEY: { default: "$secrets.my-key" },
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
const context = buildResolverContext({}, {}, { "my-key": "super-secret" }, NO_APP);
|
|
155
|
-
const env = resolveComponentEnv(component, context, {});
|
|
156
|
-
expect(env.SECRET_KEY).toBe("super-secret");
|
|
157
|
-
});
|
|
158
|
-
it("set_env takes precedence over component env", () => {
|
|
159
|
-
const component = {
|
|
160
|
-
requires: [
|
|
161
|
-
{
|
|
162
|
-
type: "postgres",
|
|
163
|
-
set_env: { DATABASE_URL: "$url" },
|
|
164
|
-
},
|
|
165
|
-
],
|
|
166
|
-
env: {
|
|
167
|
-
DATABASE_URL: { default: "fallback" },
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
const resourceMap = {
|
|
171
|
-
postgres: {
|
|
172
|
-
url: "postgresql://real-url",
|
|
173
|
-
host: "localhost",
|
|
174
|
-
port: 5432,
|
|
175
|
-
},
|
|
176
|
-
};
|
|
177
|
-
const context = buildResolverContext(resourceMap, {}, {}, NO_APP);
|
|
178
|
-
const env = resolveComponentEnv(component, context, resourceMap);
|
|
179
|
-
expect(env.DATABASE_URL).toBe("postgresql://real-url");
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
describe("generateSecrets", () => {
|
|
183
|
-
it("generates new secrets", async () => {
|
|
184
|
-
const defs = {
|
|
185
|
-
"jwt-secret": { generator: "secret" },
|
|
186
|
-
"app-uuid": { generator: "uuid" },
|
|
187
|
-
};
|
|
188
|
-
const secrets = await generateSecrets(defs, {});
|
|
189
|
-
expect(secrets["jwt-secret"]).toBeTruthy();
|
|
190
|
-
expect(secrets["app-uuid"]).toMatch(/^[0-9a-f-]+$/);
|
|
191
|
-
});
|
|
192
|
-
it("reuses existing secrets", async () => {
|
|
193
|
-
const defs = {
|
|
194
|
-
"jwt-secret": { generator: "secret" },
|
|
195
|
-
};
|
|
196
|
-
const existing = { "jwt-secret": "keep-this" };
|
|
197
|
-
const secrets = await generateSecrets(defs, existing);
|
|
198
|
-
expect(secrets["jwt-secret"]).toBe("keep-this");
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
//# sourceMappingURL=env-writer.test.js.map
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { detectPackageManager } from "../lockfile-detect.js";
|
|
3
|
-
import { mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
describe("detectPackageManager", () => {
|
|
7
|
-
it("returns null for empty directory", async () => {
|
|
8
|
-
const dir = await mkdtemp(join(tmpdir(), "lf-test-"));
|
|
9
|
-
try {
|
|
10
|
-
const pm = await detectPackageManager(dir);
|
|
11
|
-
expect(pm).toBeNull();
|
|
12
|
-
}
|
|
13
|
-
finally {
|
|
14
|
-
await rm(dir, { recursive: true });
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
it("detects bun from bun.lockb", async () => {
|
|
18
|
-
const dir = await mkdtemp(join(tmpdir(), "lf-test-"));
|
|
19
|
-
try {
|
|
20
|
-
await writeFile(join(dir, "bun.lockb"), "");
|
|
21
|
-
const pm = await detectPackageManager(dir);
|
|
22
|
-
expect(pm?.name).toBe("bun");
|
|
23
|
-
expect(pm?.installCommand).toBe("bun install");
|
|
24
|
-
}
|
|
25
|
-
finally {
|
|
26
|
-
await rm(dir, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
it("detects yarn from yarn.lock", async () => {
|
|
30
|
-
const dir = await mkdtemp(join(tmpdir(), "lf-test-"));
|
|
31
|
-
try {
|
|
32
|
-
await writeFile(join(dir, "yarn.lock"), "");
|
|
33
|
-
const pm = await detectPackageManager(dir);
|
|
34
|
-
expect(pm?.name).toBe("yarn");
|
|
35
|
-
}
|
|
36
|
-
finally {
|
|
37
|
-
await rm(dir, { recursive: true });
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
it("detects pnpm from pnpm-lock.yaml", async () => {
|
|
41
|
-
const dir = await mkdtemp(join(tmpdir(), "lf-test-"));
|
|
42
|
-
try {
|
|
43
|
-
await writeFile(join(dir, "pnpm-lock.yaml"), "");
|
|
44
|
-
const pm = await detectPackageManager(dir);
|
|
45
|
-
expect(pm?.name).toBe("pnpm");
|
|
46
|
-
}
|
|
47
|
-
finally {
|
|
48
|
-
await rm(dir, { recursive: true });
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
it("detects bundler from Gemfile.lock", async () => {
|
|
52
|
-
const dir = await mkdtemp(join(tmpdir(), "lf-test-"));
|
|
53
|
-
try {
|
|
54
|
-
await writeFile(join(dir, "Gemfile.lock"), "");
|
|
55
|
-
const pm = await detectPackageManager(dir);
|
|
56
|
-
expect(pm?.name).toBe("bundler");
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
await rm(dir, { recursive: true });
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
it("prefers bun over yarn when both present", async () => {
|
|
63
|
-
const dir = await mkdtemp(join(tmpdir(), "lf-test-"));
|
|
64
|
-
try {
|
|
65
|
-
await writeFile(join(dir, "bun.lockb"), "");
|
|
66
|
-
await writeFile(join(dir, "yarn.lock"), "");
|
|
67
|
-
const pm = await detectPackageManager(dir);
|
|
68
|
-
expect(pm?.name).toBe("bun");
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
await rm(dir, { recursive: true });
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
//# sourceMappingURL=lockfile-detect.test.js.map
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { allocatePort, allocatePorts } from "../port-allocator.js";
|
|
3
|
-
describe("allocatePort", () => {
|
|
4
|
-
it("returns a port in the expected range", async () => {
|
|
5
|
-
const port = await allocatePort("test-app", new Set());
|
|
6
|
-
expect(port).toBeGreaterThanOrEqual(10_000);
|
|
7
|
-
expect(port).toBeLessThan(20_000);
|
|
8
|
-
});
|
|
9
|
-
it("is deterministic for the same key", async () => {
|
|
10
|
-
const port1 = await allocatePort("my-app", new Set());
|
|
11
|
-
const port2 = await allocatePort("my-app", new Set());
|
|
12
|
-
expect(port1).toBe(port2);
|
|
13
|
-
});
|
|
14
|
-
it("avoids ports already in use", async () => {
|
|
15
|
-
const port1 = await allocatePort("collision-test", new Set());
|
|
16
|
-
const port2 = await allocatePort("collision-test", new Set([port1]));
|
|
17
|
-
expect(port2).not.toBe(port1);
|
|
18
|
-
});
|
|
19
|
-
it("produces different ports for different keys", async () => {
|
|
20
|
-
const port1 = await allocatePort("app-a", new Set());
|
|
21
|
-
const port2 = await allocatePort("app-b", new Set());
|
|
22
|
-
// Not guaranteed but overwhelmingly likely for different inputs
|
|
23
|
-
expect(port1).not.toBe(port2);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe("allocatePorts", () => {
|
|
27
|
-
it("allocates ports for all components", async () => {
|
|
28
|
-
const components = {
|
|
29
|
-
backend: { provides: [{ port: 3000 }] },
|
|
30
|
-
frontend: { provides: [{ port: 3001 }] },
|
|
31
|
-
};
|
|
32
|
-
const ports = await allocatePorts(components, "test-app");
|
|
33
|
-
expect(ports.backend).toBeDefined();
|
|
34
|
-
expect(ports.frontend).toBeDefined();
|
|
35
|
-
expect(ports.backend).not.toBe(ports.frontend);
|
|
36
|
-
});
|
|
37
|
-
it("prefers the declared port if free", async () => {
|
|
38
|
-
const components = {
|
|
39
|
-
api: { provides: [{ port: 4567 }] },
|
|
40
|
-
};
|
|
41
|
-
const ports = await allocatePorts(components, "test-app");
|
|
42
|
-
expect(ports.api).toBe(4567);
|
|
43
|
-
});
|
|
44
|
-
it("reuses saved ports when available", async () => {
|
|
45
|
-
const components = {
|
|
46
|
-
api: { provides: [{ port: 3000 }] },
|
|
47
|
-
};
|
|
48
|
-
const saved = { api: 9876 };
|
|
49
|
-
const ports = await allocatePorts(components, "test-app", saved);
|
|
50
|
-
expect(ports.api).toBe(9876);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
//# sourceMappingURL=port-allocator.test.js.map
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { generateValue, generatePassword } from "../secret-generator.js";
|
|
3
|
-
describe("generateValue", () => {
|
|
4
|
-
it("generates a base64url secret", async () => {
|
|
5
|
-
const value = await generateValue("secret");
|
|
6
|
-
expect(value).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
7
|
-
expect(value.length).toBeGreaterThan(20);
|
|
8
|
-
});
|
|
9
|
-
it("generates a UUID", async () => {
|
|
10
|
-
const value = await generateValue("uuid");
|
|
11
|
-
expect(value).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
|
|
12
|
-
});
|
|
13
|
-
it("generates unique values each time", async () => {
|
|
14
|
-
const a = await generateValue("secret");
|
|
15
|
-
const b = await generateValue("secret");
|
|
16
|
-
expect(a).not.toBe(b);
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
describe("generatePassword", () => {
|
|
20
|
-
it("generates a URL-safe password", () => {
|
|
21
|
-
const pw = generatePassword();
|
|
22
|
-
expect(pw).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
23
|
-
expect(pw.length).toBeGreaterThan(10);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
//# sourceMappingURL=secret-generator.test.js.map
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { initState, hashLaunchfile } from "../state.js";
|
|
3
|
-
describe("initState", () => {
|
|
4
|
-
it("creates a state with correct app name", () => {
|
|
5
|
-
const state = initState("my-app", "name: my-app");
|
|
6
|
-
expect(state.appName).toBe("my-app");
|
|
7
|
-
expect(state.version).toBe(1);
|
|
8
|
-
expect(state.resources).toEqual({});
|
|
9
|
-
expect(state.secrets).toEqual({});
|
|
10
|
-
expect(state.ports).toEqual({});
|
|
11
|
-
});
|
|
12
|
-
it("hashes launchfile content", () => {
|
|
13
|
-
const state = initState("app", "some yaml content");
|
|
14
|
-
expect(state.launchfileHash).toBeTruthy();
|
|
15
|
-
expect(state.launchfileHash.length).toBe(16);
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
describe("hashLaunchfile", () => {
|
|
19
|
-
it("is deterministic", () => {
|
|
20
|
-
const a = hashLaunchfile("hello");
|
|
21
|
-
const b = hashLaunchfile("hello");
|
|
22
|
-
expect(a).toBe(b);
|
|
23
|
-
});
|
|
24
|
-
it("differs for different content", () => {
|
|
25
|
-
const a = hashLaunchfile("hello");
|
|
26
|
-
const b = hashLaunchfile("world");
|
|
27
|
-
expect(a).not.toBe(b);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
//# sourceMappingURL=state.test.js.map
|