@oore/client 0.12.0 → 0.13.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.
Files changed (28) hide show
  1. package/README.md +37 -0
  2. package/dist/cli/config.js +57 -0
  3. package/dist/cli/main.js +17 -0
  4. package/dist/cli/qr.js +327 -0
  5. package/dist/cli/run.js +438 -0
  6. package/dist/generated/types.gen.d.ts +29 -0
  7. package/dist/generated/valibot/v-create-notification-channel-body.d.ts +4 -0
  8. package/dist/generated/valibot/v-create-notification-channel-request.d.ts +4 -0
  9. package/dist/generated/valibot/v-create-notification-channel-request.js +2 -0
  10. package/dist/generated/valibot/v-create-notification-channel-response.d.ts +4 -0
  11. package/dist/generated/valibot/v-get-notification-channel-response.d.ts +4 -0
  12. package/dist/generated/valibot/v-list-notification-channels-response.d.ts +4 -0
  13. package/dist/generated/valibot/v-list-notification-channels-response2.d.ts +4 -0
  14. package/dist/generated/valibot/v-notification-channel-response.d.ts +4 -0
  15. package/dist/generated/valibot/v-notification-channel.d.ts +4 -0
  16. package/dist/generated/valibot/v-notification-channel.js +2 -0
  17. package/dist/generated/valibot/v-slack-mention.d.ts +5 -0
  18. package/dist/generated/valibot/v-slack-mention.js +6 -0
  19. package/dist/generated/valibot/v-update-notification-channel-body.d.ts +4 -0
  20. package/dist/generated/valibot/v-update-notification-channel-request.d.ts +4 -0
  21. package/dist/generated/valibot/v-update-notification-channel-request.js +2 -0
  22. package/dist/generated/valibot/v-update-notification-channel-response.d.ts +4 -0
  23. package/dist/msw-scenarios.d.ts +12 -1
  24. package/dist/msw-scenarios.js +345 -1
  25. package/dist/valibot.d.ts +1 -0
  26. package/dist/valibot.js +1 -0
  27. package/openapi/openapi.json +42 -0
  28. package/package.json +5 -2
@@ -1,5 +1,6 @@
1
1
  // Generated by scripts/generate-valibot-modules.ts. Do not edit.
2
2
  import * as v from 'valibot';
3
+ import { vSlackMention } from './v-slack-mention.js';
3
4
  import { vUpdateSmtpConfig } from './v-update-smtp-config.js';
4
5
  export const vUpdateNotificationChannelRequest = v.object({
5
6
  enabled: v.nullish(v.boolean()),
@@ -7,6 +8,7 @@ export const vUpdateNotificationChannelRequest = v.object({
7
8
  name: v.nullish(v.string()),
8
9
  project_id: v.nullish(v.string()),
9
10
  secret: v.nullish(v.string()),
11
+ slack_mentions: v.nullish(v.array(vSlackMention)),
10
12
  smtp_config: v.nullish(vUpdateSmtpConfig),
11
13
  url: v.nullish(v.string())
12
14
  });
@@ -12,6 +12,10 @@ export declare const vUpdateNotificationChannelResponse: v.ObjectSchema<{
12
12
  readonly id: v.StringSchema<undefined>;
13
13
  readonly name: v.StringSchema<undefined>;
14
14
  readonly project_id: v.NullishSchema<v.StringSchema<undefined>, undefined>;
15
+ readonly slack_mentions: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
16
+ readonly identity: v.StringSchema<undefined>;
17
+ readonly member_id: v.StringSchema<undefined>;
18
+ }, undefined>, undefined>, undefined>;
15
19
  readonly smtp_summary: v.NullishSchema<v.ObjectSchema<{
16
20
  readonly from_address: v.StringSchema<undefined>;
17
21
  readonly host: v.StringSchema<undefined>;
@@ -1,11 +1,22 @@
1
1
  import { type HttpHandler } from 'msw';
2
2
  import { type OoreMswHandlerOptions } from './msw.js';
3
- import type { Project } from './generated/types.gen.js';
3
+ import type { Project, ReleaseDetailResponse } from './generated/types.gen.js';
4
4
  export interface OoreMockScenario {
5
5
  /** Fixed epoch seconds used for records created during the scenario. */
6
6
  now: number;
7
7
  projects: ReadonlyArray<Project>;
8
+ /**
9
+ * Releases with their history, newest history entry last. The handlers
10
+ * recompute `allowed_actions` for a maintainer from each release's state.
11
+ */
12
+ releases?: ReadonlyArray<ReleaseDetailResponse>;
8
13
  }
14
+ /** A Google Play production rollout at 20%, healthy and still moving. */
15
+ export declare const rollingReleaseScenario: ReleaseDetailResponse;
16
+ /** A Google Play rollout the daemon halted at 5% after a crash spike. */
17
+ export declare const haltedReleaseScenario: ReleaseDetailResponse;
18
+ /** An App Store release that finished its phased rollout. */
19
+ export declare const completeReleaseScenario: ReleaseDetailResponse;
9
20
  export declare const defaultOoreScenario: OoreMockScenario;
10
21
  /**
11
22
  * Creates deterministic, stateful handlers for Oore's core demo scenario.
@@ -1,5 +1,159 @@
1
1
  import { HttpResponse } from 'msw';
2
- import { createProjectMock, listProjectsMock, } from './msw.js';
2
+ import { approveReleaseMock, completeReleaseMock, createProjectMock, getReleaseMock, haltReleaseMock, listProjectsMock, listReleaseEventsMock, listReleasesMock, pauseReleaseMock, raiseReleaseMock, resumeReleaseMock, } from './msw.js';
3
+ const DEFAULT_RELEASE_POLICY = {
4
+ approval: 'none',
5
+ crash_rate_threshold: 0.02,
6
+ interval_hours: 24,
7
+ steps: [0.01, 0.05, 0.2, 0.5, 1],
8
+ };
9
+ /** A Google Play production rollout at 20%, healthy and still moving. */
10
+ export const rollingReleaseScenario = {
11
+ history: [
12
+ {
13
+ at: 1_767_052_800,
14
+ id: 'release-rolling-h1',
15
+ to_fraction: 0.01,
16
+ to_state: 'rolling',
17
+ },
18
+ {
19
+ at: 1_767_139_200,
20
+ from_fraction: 0.01,
21
+ from_state: 'rolling',
22
+ id: 'release-rolling-h2',
23
+ to_fraction: 0.05,
24
+ to_state: 'rolling',
25
+ },
26
+ {
27
+ actor_email: 'owner@acme.test',
28
+ actor_id: 'demo-owner',
29
+ at: 1_767_200_000,
30
+ from_fraction: 0.05,
31
+ from_state: 'rolling',
32
+ id: 'release-rolling-h3',
33
+ reason: 'Crash-free rate looks good',
34
+ to_fraction: 0.2,
35
+ to_state: 'rolling',
36
+ },
37
+ ],
38
+ release: {
39
+ allowed_actions: [],
40
+ artifact_id: 'artifact-rolling',
41
+ awaiting_approval: false,
42
+ build_id: 'build-142',
43
+ build_number: 142,
44
+ crash_rate: 0.004,
45
+ crash_rate_threshold: 0.02,
46
+ created_at: 1_767_052_800,
47
+ health: 'healthy',
48
+ id: 'release-rolling',
49
+ next_fraction: 0.5,
50
+ next_step_at: 1_767_286_400,
51
+ package_name: 'com.acme.fluttershop',
52
+ platform: 'android',
53
+ policy: DEFAULT_RELEASE_POLICY,
54
+ project_id: 'project-1',
55
+ project_name: 'FlutterShop',
56
+ rollout_fraction: 0.2,
57
+ state: 'rolling',
58
+ store: 'google_play',
59
+ track: 'production',
60
+ updated_at: 1_767_200_000,
61
+ version_code: 142,
62
+ version_name: '2.4.0',
63
+ },
64
+ };
65
+ /** A Google Play rollout the daemon halted at 5% after a crash spike. */
66
+ export const haltedReleaseScenario = {
67
+ history: [
68
+ {
69
+ at: 1_766_880_000,
70
+ id: 'release-halted-h1',
71
+ to_fraction: 0.01,
72
+ to_state: 'rolling',
73
+ },
74
+ {
75
+ at: 1_766_966_400,
76
+ from_fraction: 0.01,
77
+ from_state: 'rolling',
78
+ id: 'release-halted-h2',
79
+ to_fraction: 0.05,
80
+ to_state: 'rolling',
81
+ },
82
+ {
83
+ at: 1_767_000_000,
84
+ from_fraction: 0.05,
85
+ from_state: 'rolling',
86
+ id: 'release-halted-h3',
87
+ reason: 'Crash rate 3.1% reached the 2% threshold',
88
+ to_state: 'halted',
89
+ },
90
+ ],
91
+ release: {
92
+ allowed_actions: [],
93
+ artifact_id: 'artifact-halted',
94
+ awaiting_approval: false,
95
+ build_id: 'build-139',
96
+ build_number: 139,
97
+ crash_rate: 0.031,
98
+ crash_rate_threshold: 0.02,
99
+ created_at: 1_766_880_000,
100
+ health: 'breached',
101
+ id: 'release-halted',
102
+ package_name: 'com.acme.fluttershop',
103
+ platform: 'android',
104
+ policy: DEFAULT_RELEASE_POLICY,
105
+ project_id: 'project-1',
106
+ project_name: 'FlutterShop',
107
+ rollout_fraction: 0.05,
108
+ state: 'halted',
109
+ store: 'google_play',
110
+ track: 'production',
111
+ updated_at: 1_767_000_000,
112
+ version_code: 139,
113
+ version_name: '2.3.1',
114
+ },
115
+ };
116
+ /** An App Store release that finished its phased rollout. */
117
+ export const completeReleaseScenario = {
118
+ history: [
119
+ {
120
+ at: 1_766_448_000,
121
+ id: 'release-complete-h1',
122
+ to_fraction: 0.01,
123
+ to_state: 'rolling',
124
+ },
125
+ {
126
+ at: 1_767_052_800,
127
+ from_fraction: 0.5,
128
+ from_state: 'rolling',
129
+ id: 'release-complete-h2',
130
+ to_fraction: 1,
131
+ to_state: 'complete',
132
+ },
133
+ ],
134
+ release: {
135
+ allowed_actions: [],
136
+ artifact_id: 'artifact-complete',
137
+ awaiting_approval: false,
138
+ build_id: 'build-137',
139
+ build_number: 137,
140
+ created_at: 1_766_448_000,
141
+ health: 'healthy',
142
+ id: 'release-complete',
143
+ package_name: 'com.acme.fluttershop',
144
+ platform: 'ios',
145
+ policy: DEFAULT_RELEASE_POLICY,
146
+ project_id: 'project-1',
147
+ project_name: 'FlutterShop',
148
+ rollout_fraction: 1,
149
+ state: 'complete',
150
+ store: 'app_store',
151
+ track: 'app_store',
152
+ updated_at: 1_767_052_800,
153
+ version_code: 137,
154
+ version_name: '2.3.0',
155
+ },
156
+ };
3
157
  export const defaultOoreScenario = {
4
158
  now: 1_767_225_600,
5
159
  projects: [
@@ -32,7 +186,65 @@ export const defaultOoreScenario = {
32
186
  updated_at: 1_767_139_200,
33
187
  },
34
188
  ],
189
+ releases: [
190
+ rollingReleaseScenario,
191
+ haltedReleaseScenario,
192
+ completeReleaseScenario,
193
+ ],
194
+ };
195
+ const isAwaitingApproval = (release) => {
196
+ if (release.approved_at != null)
197
+ return false;
198
+ if (release.state === 'complete' || release.state === 'failed')
199
+ return false;
200
+ switch (release.policy.approval ?? 'none') {
201
+ case 'before_upload':
202
+ return release.state === 'uploading';
203
+ case 'before_production':
204
+ return release.track === 'production';
205
+ default:
206
+ return false;
207
+ }
208
+ };
209
+ /** Mirrors the daemon's rules for a caller who may manage releases. */
210
+ const maintainerActions = (release) => {
211
+ const awaiting = isAwaitingApproval(release);
212
+ const { rollout_fraction: fraction, state } = release;
213
+ const appStore = release.store === 'app_store';
214
+ const playDraft = release.store === 'google_play' && state === 'paused' && fraction === 0;
215
+ const moving = state === 'rolling' || state === 'paused';
216
+ const actions = [];
217
+ if (!appStore && state === 'rolling' && !awaiting && fraction < 1)
218
+ actions.push('raise');
219
+ if (state === 'rolling')
220
+ actions.push('pause');
221
+ if ((state === 'paused' || state === 'halted') && !awaiting)
222
+ actions.push('resume');
223
+ if (!appStore && !playDraft && moving)
224
+ actions.push('halt');
225
+ if (moving && !awaiting)
226
+ actions.push('complete');
227
+ if (awaiting)
228
+ actions.push('approve');
229
+ return actions;
35
230
  };
231
+ const withActions = (release) => ({
232
+ ...release,
233
+ allowed_actions: maintainerActions(release),
234
+ awaiting_approval: isAwaitingApproval(release),
235
+ });
236
+ const apiError = (status, code, error) => HttpResponse.json({ code, error }, { status });
237
+ const cloneRelease = (detail) => ({
238
+ history: detail.history.map((entry) => ({ ...entry })),
239
+ release: {
240
+ ...detail.release,
241
+ allowed_actions: [...detail.release.allowed_actions],
242
+ policy: {
243
+ ...detail.release.policy,
244
+ steps: detail.release.policy.steps && [...detail.release.policy.steps],
245
+ },
246
+ },
247
+ });
36
248
  /**
37
249
  * Creates deterministic, stateful handlers for Oore's core demo scenario.
38
250
  * Each call owns an isolated copy of the supplied state.
@@ -43,6 +255,86 @@ export const createOoreMockHandlers = (scenario = defaultOoreScenario, options)
43
255
  settings: { ...project.settings },
44
256
  }));
45
257
  let nextProjectNumber = projects.length + 1;
258
+ const releases = (scenario.releases ?? []).map(cloneRelease);
259
+ let clock = scenario.now;
260
+ let nextHistoryNumber = 1;
261
+ const releaseAction = (action) => async ({ params, request, }) => {
262
+ const detail = releases.find((item) => item.release.id === params.release_id);
263
+ if (!detail)
264
+ return apiError(404, 'not_found', 'Release not found');
265
+ const text = await request.text();
266
+ const body = text ? JSON.parse(text) : {};
267
+ const current = detail.release;
268
+ if (body.fraction != null && action !== 'raise')
269
+ return apiError(400, 'invalid_input', 'fraction is only accepted by raise');
270
+ if (!maintainerActions(current).includes(action))
271
+ return apiError(409, 'invalid_release_state', `Cannot ${action} a release that is ${current.state}`);
272
+ clock += 60;
273
+ if (action === 'approve') {
274
+ detail.release = withActions({
275
+ ...current,
276
+ approved_at: clock,
277
+ approved_by_email: 'owner@acme.test',
278
+ updated_at: clock,
279
+ });
280
+ return HttpResponse.json(detail.release);
281
+ }
282
+ let toState;
283
+ let toFraction;
284
+ switch (action) {
285
+ case 'raise': {
286
+ const fraction = body.fraction ??
287
+ current.policy.steps?.find((step) => step > current.rollout_fraction) ??
288
+ 1;
289
+ if (!Number.isFinite(fraction) ||
290
+ fraction <= current.rollout_fraction ||
291
+ fraction > 1)
292
+ return apiError(400, 'invalid_input', 'fraction must be above the current rollout and at most 1');
293
+ toFraction = fraction;
294
+ toState = fraction >= 1 ? 'complete' : 'rolling';
295
+ break;
296
+ }
297
+ case 'pause':
298
+ toState = 'paused';
299
+ break;
300
+ case 'halt':
301
+ toState = 'halted';
302
+ break;
303
+ case 'resume':
304
+ toState = 'rolling';
305
+ if (current.rollout_fraction === 0)
306
+ toFraction = current.policy.steps?.[0];
307
+ break;
308
+ default:
309
+ toState = 'complete';
310
+ toFraction = 1;
311
+ }
312
+ const entry = {
313
+ actor_email: 'owner@acme.test',
314
+ actor_id: 'demo-owner',
315
+ at: clock,
316
+ from_fraction: current.rollout_fraction,
317
+ from_state: current.state,
318
+ id: `release-history-${nextHistoryNumber++}`,
319
+ reason: body.reason ?? null,
320
+ to_fraction: toFraction ?? null,
321
+ to_state: toState,
322
+ };
323
+ detail.history.push(entry);
324
+ const rolloutFraction = toFraction ?? current.rollout_fraction;
325
+ const policySteps = current.policy.steps ?? [];
326
+ detail.release = withActions({
327
+ ...current,
328
+ next_fraction: toState === 'rolling'
329
+ ? (policySteps.find((step) => step > rolloutFraction) ?? null)
330
+ : null,
331
+ next_step_at: null,
332
+ rollout_fraction: rolloutFraction,
333
+ state: toState,
334
+ updated_at: clock,
335
+ });
336
+ return HttpResponse.json(detail.release);
337
+ };
46
338
  return [
47
339
  listProjectsMock(({ request }) => {
48
340
  const url = new URL(request.url);
@@ -74,5 +366,57 @@ export const createOoreMockHandlers = (scenario = defaultOoreScenario, options)
74
366
  projects.push(project);
75
367
  return HttpResponse.json({ project }, { status: 201 });
76
368
  }, options),
369
+ listReleasesMock(({ request }) => {
370
+ const url = new URL(request.url);
371
+ const projectId = url.searchParams.get('project_id');
372
+ const buildId = url.searchParams.get('build_id');
373
+ const states = url.searchParams.get('state')?.split(',');
374
+ const offset = Number(url.searchParams.get('offset') ?? 0);
375
+ const limit = Number(url.searchParams.get('limit') ?? 50);
376
+ const matching = releases
377
+ .map((detail) => detail.release)
378
+ .filter((release) => (!projectId || release.project_id === projectId) &&
379
+ (!buildId || release.build_id === buildId) &&
380
+ (!states || states.includes(release.state)))
381
+ .sort((a, b) => b.created_at - a.created_at);
382
+ return HttpResponse.json({
383
+ releases: matching.slice(offset, offset + limit).map(withActions),
384
+ total: matching.length,
385
+ });
386
+ }, options),
387
+ getReleaseMock(({ params }) => {
388
+ const detail = releases.find((item) => item.release.id === params.release_id);
389
+ if (!detail)
390
+ return apiError(404, 'not_found', 'Release not found');
391
+ return HttpResponse.json({
392
+ history: detail.history,
393
+ release: withActions(detail.release),
394
+ });
395
+ }, options),
396
+ listReleaseEventsMock(({ request }) => {
397
+ const url = new URL(request.url);
398
+ const projectId = url.searchParams.get('project_id');
399
+ const limit = Number(url.searchParams.get('limit') ?? 50);
400
+ const events = releases
401
+ .filter((detail) => !projectId || detail.release.project_id === projectId)
402
+ .flatMap(({ history, release }) => history.map((entry) => ({
403
+ ...entry,
404
+ build_id: release.build_id,
405
+ project_id: release.project_id,
406
+ project_name: release.project_name,
407
+ release_id: release.id,
408
+ store: release.store,
409
+ track: release.track,
410
+ version_name: release.version_name,
411
+ })))
412
+ .sort((a, b) => b.at - a.at);
413
+ return HttpResponse.json({ events: events.slice(0, limit) });
414
+ }, options),
415
+ raiseReleaseMock(releaseAction('raise'), options),
416
+ pauseReleaseMock(releaseAction('pause'), options),
417
+ resumeReleaseMock(releaseAction('resume'), options),
418
+ haltReleaseMock(releaseAction('halt'), options),
419
+ completeReleaseMock(releaseAction('complete'), options),
420
+ approveReleaseMock(releaseAction('approve'), options),
77
421
  ];
78
422
  };
package/dist/valibot.d.ts CHANGED
@@ -245,6 +245,7 @@ export { vSetupSummaryResponse } from './generated/valibot/v-setup-summary-respo
245
245
  export { vSetupTrustedProxyClaimOwnerResponse } from './generated/valibot/v-setup-trusted-proxy-claim-owner-response.js';
246
246
  export { vSetupTrustedProxyConfigureRequest } from './generated/valibot/v-setup-trusted-proxy-configure-request.js';
247
247
  export { vSetupTrustedProxyConfigureResponse } from './generated/valibot/v-setup-trusted-proxy-configure-response.js';
248
+ export { vSlackMention } from './generated/valibot/v-slack-mention.js';
248
249
  export { vSmtpTlsMode } from './generated/valibot/v-smtp-tls-mode.js';
249
250
  export { vSmtpConfig } from './generated/valibot/v-smtp-config.js';
250
251
  export { vCreateNotificationChannelRequest } from './generated/valibot/v-create-notification-channel-request.js';
package/dist/valibot.js CHANGED
@@ -246,6 +246,7 @@ export { vSetupSummaryResponse } from './generated/valibot/v-setup-summary-respo
246
246
  export { vSetupTrustedProxyClaimOwnerResponse } from './generated/valibot/v-setup-trusted-proxy-claim-owner-response.js';
247
247
  export { vSetupTrustedProxyConfigureRequest } from './generated/valibot/v-setup-trusted-proxy-configure-request.js';
248
248
  export { vSetupTrustedProxyConfigureResponse } from './generated/valibot/v-setup-trusted-proxy-configure-response.js';
249
+ export { vSlackMention } from './generated/valibot/v-slack-mention.js';
249
250
  export { vSmtpTlsMode } from './generated/valibot/v-smtp-tls-mode.js';
250
251
  export { vSmtpConfig } from './generated/valibot/v-smtp-config.js';
251
252
  export { vCreateNotificationChannelRequest } from './generated/valibot/v-create-notification-channel-request.js';
@@ -14046,6 +14046,13 @@
14046
14046
  ],
14047
14047
  "description": "Webhook: HMAC secret for signing payloads. Slack: bot token with\n`users:read.email`, used to mention the triggering user. Optional."
14048
14048
  },
14049
+ "slack_mentions": {
14050
+ "type": "array",
14051
+ "items": {
14052
+ "$ref": "#/components/schemas/SlackMention"
14053
+ },
14054
+ "description": "Slack member IDs to mention (Slack only). Works without a bot token."
14055
+ },
14049
14056
  "smtp_config": {
14050
14057
  "oneOf": [
14051
14058
  {
@@ -16043,6 +16050,13 @@
16043
16050
  ],
16044
16051
  "description": "Project this channel is scoped to. Absent means the channel receives\nevents from every project, plus instance-wide events such as runner\noffline."
16045
16052
  },
16053
+ "slack_mentions": {
16054
+ "type": "array",
16055
+ "items": {
16056
+ "$ref": "#/components/schemas/SlackMention"
16057
+ },
16058
+ "description": "Slack member IDs to mention, matched against the person behind each\nevent (Slack only). Checked before the bot token lookup. Absent from\ndaemons that predate it, which is the same as none."
16059
+ },
16046
16060
  "smtp_summary": {
16047
16061
  "oneOf": [
16048
16062
  {
@@ -19131,6 +19145,24 @@
19131
19145
  }
19132
19146
  }
19133
19147
  },
19148
+ "SlackMention": {
19149
+ "type": "object",
19150
+ "description": "Maps a person to the Slack member mentioned in their notifications.",
19151
+ "required": [
19152
+ "identity",
19153
+ "member_id"
19154
+ ],
19155
+ "properties": {
19156
+ "identity": {
19157
+ "type": "string",
19158
+ "description": "Oore sign-in email (manual and API builds, releases) or Git username\n(push and merge request builds). Matched without regard to case."
19159
+ },
19160
+ "member_id": {
19161
+ "type": "string",
19162
+ "description": "Slack member ID, such as `U0123ABCD`."
19163
+ }
19164
+ }
19165
+ },
19134
19166
  "SmtpConfig": {
19135
19167
  "type": "object",
19136
19168
  "required": [
@@ -19950,6 +19982,16 @@
19950
19982
  ],
19951
19983
  "description": "New HMAC secret (webhook) or bot token (slack). Omit to keep existing;\npass empty string to clear."
19952
19984
  },
19985
+ "slack_mentions": {
19986
+ "type": [
19987
+ "array",
19988
+ "null"
19989
+ ],
19990
+ "items": {
19991
+ "$ref": "#/components/schemas/SlackMention"
19992
+ },
19993
+ "description": "New Slack member mappings (Slack only). Omit to keep existing; pass an\nempty list to clear."
19994
+ },
19953
19995
  "smtp_config": {
19954
19996
  "oneOf": [
19955
19997
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oore/client",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Typed, tree-shakeable client for the Oore API",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -19,6 +19,9 @@
19
19
  "publishConfig": {
20
20
  "access": "public"
21
21
  },
22
+ "bin": {
23
+ "oore-dev": "./dist/cli/main.js"
24
+ },
22
25
  "exports": {
23
26
  ".": {
24
27
  "types": "./dist/index.d.ts",
@@ -70,7 +73,7 @@
70
73
  "README.md"
71
74
  ],
72
75
  "scripts": {
73
- "build": "bun run clean && bun run generate && bun run typecheck && bunx tsc -p tsconfig.build.json",
76
+ "build": "bun run clean && bun run generate && bun run typecheck && bunx tsc -p tsconfig.build.json && bunx tsc -p tsconfig.cli.json",
74
77
  "check": "bun run generate && bun run typecheck && bun run check:coverage && bun test && bun run build && bun run check:exports && bun run check:tree-shaking",
75
78
  "check:coverage": "bun run scripts/check-coverage.ts",
76
79
  "check:exports": "bun run scripts/check-exports.ts",