instant-cli 1.0.33 → 1.0.34
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/.turbo/turbo-build.log +1 -1
- package/__tests__/multiSelect.test.ts +236 -0
- package/__tests__/select.test.ts +224 -0
- package/__tests__/webhooks.test.ts +728 -0
- package/dist/commands/webhooks/add.d.ts +9 -0
- package/dist/commands/webhooks/add.d.ts.map +1 -0
- package/dist/commands/webhooks/add.js +75 -0
- package/dist/commands/webhooks/add.js.map +1 -0
- package/dist/commands/webhooks/delete.d.ts +6 -0
- package/dist/commands/webhooks/delete.d.ts.map +1 -0
- package/dist/commands/webhooks/delete.js +17 -0
- package/dist/commands/webhooks/delete.js.map +1 -0
- package/dist/commands/webhooks/disable.d.ts +7 -0
- package/dist/commands/webhooks/disable.d.ts.map +1 -0
- package/dist/commands/webhooks/disable.js +18 -0
- package/dist/commands/webhooks/disable.js.map +1 -0
- package/dist/commands/webhooks/enable.d.ts +6 -0
- package/dist/commands/webhooks/enable.d.ts.map +1 -0
- package/dist/commands/webhooks/enable.js +18 -0
- package/dist/commands/webhooks/enable.js.map +1 -0
- package/dist/commands/webhooks/events/list.d.ts +7 -0
- package/dist/commands/webhooks/events/list.d.ts.map +1 -0
- package/dist/commands/webhooks/events/list.js +31 -0
- package/dist/commands/webhooks/events/list.js.map +1 -0
- package/dist/commands/webhooks/events/payload.d.ts +8 -0
- package/dist/commands/webhooks/events/payload.d.ts.map +1 -0
- package/dist/commands/webhooks/events/payload.js +39 -0
- package/dist/commands/webhooks/events/payload.js.map +1 -0
- package/dist/commands/webhooks/events/resend.d.ts +8 -0
- package/dist/commands/webhooks/events/resend.d.ts.map +1 -0
- package/dist/commands/webhooks/events/resend.js +43 -0
- package/dist/commands/webhooks/events/resend.js.map +1 -0
- package/dist/commands/webhooks/list.d.ts +8 -0
- package/dist/commands/webhooks/list.d.ts.map +1 -0
- package/dist/commands/webhooks/list.js +29 -0
- package/dist/commands/webhooks/list.js.map +1 -0
- package/dist/commands/webhooks/shared.d.ts +40 -0
- package/dist/commands/webhooks/shared.d.ts.map +1 -0
- package/dist/commands/webhooks/shared.js +248 -0
- package/dist/commands/webhooks/shared.js.map +1 -0
- package/dist/commands/webhooks/update.d.ts +10 -0
- package/dist/commands/webhooks/update.d.ts.map +1 -0
- package/dist/commands/webhooks/update.js +189 -0
- package/dist/commands/webhooks/update.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -0
- package/dist/index.js.map +1 -1
- package/dist/layer.d.ts +2 -2
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +30 -1
- package/dist/layer.js.map +1 -1
- package/dist/lib/webhooks.d.ts +28 -0
- package/dist/lib/webhooks.d.ts.map +1 -0
- package/dist/lib/webhooks.js +102 -0
- package/dist/lib/webhooks.js.map +1 -0
- package/dist/ui/index.d.ts +39 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +387 -25
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/lib.d.ts +7 -0
- package/dist/ui/lib.d.ts.map +1 -1
- package/dist/ui/lib.js +40 -1
- package/dist/ui/lib.js.map +1 -1
- package/package.json +4 -4
- package/src/commands/webhooks/add.ts +111 -0
- package/src/commands/webhooks/delete.ts +23 -0
- package/src/commands/webhooks/disable.ts +24 -0
- package/src/commands/webhooks/enable.ts +24 -0
- package/src/commands/webhooks/events/list.ts +38 -0
- package/src/commands/webhooks/events/payload.ts +56 -0
- package/src/commands/webhooks/events/resend.ts +66 -0
- package/src/commands/webhooks/list.ts +41 -0
- package/src/commands/webhooks/shared.ts +339 -0
- package/src/commands/webhooks/update.ts +276 -0
- package/src/index.ts +242 -0
- package/src/layer.ts +33 -1
- package/src/lib/webhooks.ts +127 -0
- package/src/ui/index.ts +465 -32
- package/src/ui/lib.ts +41 -1
package/src/index.ts
CHANGED
|
@@ -36,6 +36,15 @@ import { authOriginAddCmd } from './commands/auth/origin/add.ts';
|
|
|
36
36
|
import { link } from './logging.ts';
|
|
37
37
|
import { appListCommand } from './commands/app/list.ts';
|
|
38
38
|
import { appDeleteCommand } from './commands/app/delete.ts';
|
|
39
|
+
import { webhooksListCmd } from './commands/webhooks/list.ts';
|
|
40
|
+
import { webhooksAddCmd } from './commands/webhooks/add.ts';
|
|
41
|
+
import { webhooksUpdateCmd } from './commands/webhooks/update.ts';
|
|
42
|
+
import { webhooksDeleteCmd } from './commands/webhooks/delete.ts';
|
|
43
|
+
import { webhooksEnableCmd } from './commands/webhooks/enable.ts';
|
|
44
|
+
import { webhooksDisableCmd } from './commands/webhooks/disable.ts';
|
|
45
|
+
import { webhooksEventsListCmd } from './commands/webhooks/events/list.ts';
|
|
46
|
+
import { webhooksEventsPayloadCmd } from './commands/webhooks/events/payload.ts';
|
|
47
|
+
import { webhooksEventsResendCmd } from './commands/webhooks/events/resend.ts';
|
|
39
48
|
|
|
40
49
|
export type OptsFromCommand<C> =
|
|
41
50
|
C extends Command<any, infer R, any> ? R : never;
|
|
@@ -392,6 +401,239 @@ export const authOriginDeleteDef = authOrigin
|
|
|
392
401
|
);
|
|
393
402
|
});
|
|
394
403
|
|
|
404
|
+
const webhooks = program
|
|
405
|
+
.command('webhook')
|
|
406
|
+
.description('Manage webhooks for an app');
|
|
407
|
+
|
|
408
|
+
export const webhooksListDef = webhooks
|
|
409
|
+
.command('list')
|
|
410
|
+
.description('List webhooks for an app')
|
|
411
|
+
.option(
|
|
412
|
+
'-a --app <app-id>',
|
|
413
|
+
'App ID to list webhooks for. Defaults to *_INSTANT_APP_ID in .env',
|
|
414
|
+
)
|
|
415
|
+
.option('--json', 'Enable JSON output')
|
|
416
|
+
.action((opts) => {
|
|
417
|
+
return runCommandEffect(
|
|
418
|
+
webhooksListCmd(opts).pipe(
|
|
419
|
+
Effect.provide(
|
|
420
|
+
WithAppLayer({
|
|
421
|
+
coerce: false,
|
|
422
|
+
coerceAuth: false,
|
|
423
|
+
appId: opts.app,
|
|
424
|
+
allowAdminToken: false,
|
|
425
|
+
}).pipe(Layer.annotateLogs('silent', !!opts.json)),
|
|
426
|
+
),
|
|
427
|
+
),
|
|
428
|
+
);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
export const webhooksAddDef = webhooks
|
|
432
|
+
.command('add')
|
|
433
|
+
.description('Add a webhook to an app')
|
|
434
|
+
.option('--url <url>', 'HTTPS endpoint to deliver events to')
|
|
435
|
+
.option(
|
|
436
|
+
'--namespaces <e1,e2>',
|
|
437
|
+
'Comma-separated list of namespaces to listen on',
|
|
438
|
+
)
|
|
439
|
+
.option(
|
|
440
|
+
'--actions <a1,a2>',
|
|
441
|
+
'Comma-separated list of actions (create, update, delete)',
|
|
442
|
+
)
|
|
443
|
+
.option(
|
|
444
|
+
'-a --app <app-id>',
|
|
445
|
+
'App ID to add a webhook to. Defaults to *_INSTANT_APP_ID in .env',
|
|
446
|
+
)
|
|
447
|
+
.action((opts) => {
|
|
448
|
+
return runCommandEffect(
|
|
449
|
+
webhooksAddCmd(opts).pipe(
|
|
450
|
+
Effect.provide(
|
|
451
|
+
WithAppLayer({
|
|
452
|
+
coerce: false,
|
|
453
|
+
coerceAuth: false,
|
|
454
|
+
appId: opts.app,
|
|
455
|
+
allowAdminToken: false,
|
|
456
|
+
}),
|
|
457
|
+
),
|
|
458
|
+
),
|
|
459
|
+
);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
export const webhooksUpdateDef = webhooks
|
|
463
|
+
.command('update')
|
|
464
|
+
.description('Update a webhook')
|
|
465
|
+
.option('--id <webhook-id>', 'Webhook ID to update')
|
|
466
|
+
.option('--url <url>', 'New HTTPS endpoint')
|
|
467
|
+
.option('--namespaces <e1,e2>', 'New comma-separated namespaces')
|
|
468
|
+
.option(
|
|
469
|
+
'--actions <a1,a2>',
|
|
470
|
+
'New comma-separated actions (create, update, delete)',
|
|
471
|
+
)
|
|
472
|
+
.option(
|
|
473
|
+
'-a --app <app-id>',
|
|
474
|
+
'App ID the webhook belongs to. Defaults to *_INSTANT_APP_ID in .env',
|
|
475
|
+
)
|
|
476
|
+
.action((opts) => {
|
|
477
|
+
return runCommandEffect(
|
|
478
|
+
webhooksUpdateCmd(opts).pipe(
|
|
479
|
+
Effect.provide(
|
|
480
|
+
WithAppLayer({
|
|
481
|
+
coerce: false,
|
|
482
|
+
coerceAuth: false,
|
|
483
|
+
appId: opts.app,
|
|
484
|
+
allowAdminToken: false,
|
|
485
|
+
}),
|
|
486
|
+
),
|
|
487
|
+
),
|
|
488
|
+
);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
export const webhooksDeleteDef = webhooks
|
|
492
|
+
.command('delete')
|
|
493
|
+
.description('Delete a webhook')
|
|
494
|
+
.option('--id <webhook-id>', 'Webhook ID to delete')
|
|
495
|
+
.option(
|
|
496
|
+
'-a --app <app-id>',
|
|
497
|
+
'App ID the webhook belongs to. Defaults to *_INSTANT_APP_ID in .env',
|
|
498
|
+
)
|
|
499
|
+
.action((opts) => {
|
|
500
|
+
return runCommandEffect(
|
|
501
|
+
webhooksDeleteCmd(opts).pipe(
|
|
502
|
+
Effect.provide(
|
|
503
|
+
WithAppLayer({
|
|
504
|
+
coerce: false,
|
|
505
|
+
coerceAuth: false,
|
|
506
|
+
appId: opts.app,
|
|
507
|
+
allowAdminToken: false,
|
|
508
|
+
}),
|
|
509
|
+
),
|
|
510
|
+
),
|
|
511
|
+
);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
export const webhooksEnableDef = webhooks
|
|
515
|
+
.command('enable')
|
|
516
|
+
.description('Re-enable a disabled webhook')
|
|
517
|
+
.option('--id <webhook-id>', 'Webhook ID to enable')
|
|
518
|
+
.option(
|
|
519
|
+
'-a --app <app-id>',
|
|
520
|
+
'App ID the webhook belongs to. Defaults to *_INSTANT_APP_ID in .env',
|
|
521
|
+
)
|
|
522
|
+
.action((opts) => {
|
|
523
|
+
return runCommandEffect(
|
|
524
|
+
webhooksEnableCmd(opts).pipe(
|
|
525
|
+
Effect.provide(
|
|
526
|
+
WithAppLayer({
|
|
527
|
+
coerce: false,
|
|
528
|
+
coerceAuth: false,
|
|
529
|
+
appId: opts.app,
|
|
530
|
+
allowAdminToken: false,
|
|
531
|
+
}),
|
|
532
|
+
),
|
|
533
|
+
),
|
|
534
|
+
);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
export const webhooksDisableDef = webhooks
|
|
538
|
+
.command('disable')
|
|
539
|
+
.description('Disable an active webhook')
|
|
540
|
+
.option('--id <webhook-id>', 'Webhook ID to disable')
|
|
541
|
+
.option('--reason <reason>', 'Human-readable reason stored on the webhook')
|
|
542
|
+
.option(
|
|
543
|
+
'-a --app <app-id>',
|
|
544
|
+
'App ID the webhook belongs to. Defaults to *_INSTANT_APP_ID in .env',
|
|
545
|
+
)
|
|
546
|
+
.action((opts) => {
|
|
547
|
+
return runCommandEffect(
|
|
548
|
+
webhooksDisableCmd(opts).pipe(
|
|
549
|
+
Effect.provide(
|
|
550
|
+
WithAppLayer({
|
|
551
|
+
coerce: false,
|
|
552
|
+
coerceAuth: false,
|
|
553
|
+
appId: opts.app,
|
|
554
|
+
allowAdminToken: false,
|
|
555
|
+
}),
|
|
556
|
+
),
|
|
557
|
+
),
|
|
558
|
+
);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
const webhooksEvents = webhooks
|
|
562
|
+
.command('event')
|
|
563
|
+
.description('Inspect and resend webhook events');
|
|
564
|
+
|
|
565
|
+
export const webhooksEventsListDef = webhooksEvents
|
|
566
|
+
.command('list')
|
|
567
|
+
.description('List recent events for a webhook (up to 100, newest first)')
|
|
568
|
+
.option('--webhook-id <webhook-id>', 'Webhook ID to inspect')
|
|
569
|
+
.option('--json', 'Enable JSON output')
|
|
570
|
+
.option(
|
|
571
|
+
'-a --app <app-id>',
|
|
572
|
+
'App ID the webhook belongs to. Defaults to *_INSTANT_APP_ID in .env',
|
|
573
|
+
)
|
|
574
|
+
.action((opts) => {
|
|
575
|
+
return runCommandEffect(
|
|
576
|
+
webhooksEventsListCmd(opts).pipe(
|
|
577
|
+
Effect.provide(
|
|
578
|
+
WithAppLayer({
|
|
579
|
+
coerce: false,
|
|
580
|
+
coerceAuth: false,
|
|
581
|
+
appId: opts.app,
|
|
582
|
+
allowAdminToken: false,
|
|
583
|
+
}).pipe(Layer.annotateLogs('silent', !!opts.json)),
|
|
584
|
+
),
|
|
585
|
+
),
|
|
586
|
+
);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
export const webhooksEventsResendDef = webhooksEvents
|
|
590
|
+
.command('resend')
|
|
591
|
+
.description('Re-queue a webhook event for delivery')
|
|
592
|
+
.option('--webhook-id <webhook-id>', 'Webhook ID the event belongs to')
|
|
593
|
+
.option('--isn <isn>', 'Event ISN to resend')
|
|
594
|
+
.option(
|
|
595
|
+
'-a --app <app-id>',
|
|
596
|
+
'App ID the webhook belongs to. Defaults to *_INSTANT_APP_ID in .env',
|
|
597
|
+
)
|
|
598
|
+
.action((opts) => {
|
|
599
|
+
return runCommandEffect(
|
|
600
|
+
webhooksEventsResendCmd(opts).pipe(
|
|
601
|
+
Effect.provide(
|
|
602
|
+
WithAppLayer({
|
|
603
|
+
coerce: false,
|
|
604
|
+
coerceAuth: false,
|
|
605
|
+
appId: opts.app,
|
|
606
|
+
allowAdminToken: false,
|
|
607
|
+
}),
|
|
608
|
+
),
|
|
609
|
+
),
|
|
610
|
+
);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
export const webhooksEventsPayloadDef = webhooksEvents
|
|
614
|
+
.command('payload')
|
|
615
|
+
.description('Print the JSON payload for a webhook event')
|
|
616
|
+
.option('--webhook-id <webhook-id>', 'Webhook ID the event belongs to')
|
|
617
|
+
.option('--isn <isn>', 'Event ISN to fetch')
|
|
618
|
+
.option(
|
|
619
|
+
'-a --app <app-id>',
|
|
620
|
+
'App ID the webhook belongs to. Defaults to *_INSTANT_APP_ID in .env',
|
|
621
|
+
)
|
|
622
|
+
.action((opts) => {
|
|
623
|
+
return runCommandEffect(
|
|
624
|
+
webhooksEventsPayloadCmd(opts).pipe(
|
|
625
|
+
Effect.provide(
|
|
626
|
+
WithAppLayer({
|
|
627
|
+
coerce: false,
|
|
628
|
+
coerceAuth: false,
|
|
629
|
+
appId: opts.app,
|
|
630
|
+
allowAdminToken: false,
|
|
631
|
+
}),
|
|
632
|
+
),
|
|
633
|
+
),
|
|
634
|
+
);
|
|
635
|
+
});
|
|
636
|
+
|
|
395
637
|
export const initWithoutFilesDef = program
|
|
396
638
|
.command('init-without-files')
|
|
397
639
|
.description('Generate a new app id and admin token pair without any files.')
|
package/src/layer.ts
CHANGED
|
@@ -2,11 +2,12 @@ import * as NodeContext from '@effect/platform-node/NodeContext';
|
|
|
2
2
|
import * as NodeHttpClient from '@effect/platform-node/NodeHttpClient';
|
|
3
3
|
import { Cause, Effect, Layer, ManagedRuntime } from 'effect';
|
|
4
4
|
import { UnknownException } from 'effect/Cause';
|
|
5
|
+
import { inspect } from 'node:util';
|
|
5
6
|
import chalk from 'chalk';
|
|
6
7
|
import { AuthTokenLive } from './context/authToken.ts';
|
|
7
8
|
import { CurrentAppLive } from './context/currentApp.ts';
|
|
8
9
|
import { GlobalOptsLive } from './context/globalOpts.ts';
|
|
9
|
-
import { PlatformApi } from './context/platformApi.ts';
|
|
10
|
+
import { PlatformApi, PlatformApiError } from './context/platformApi.ts';
|
|
10
11
|
import {
|
|
11
12
|
PACKAGE_ALIAS_AND_FULL_NAMES,
|
|
12
13
|
ProjectInfoLive,
|
|
@@ -21,6 +22,19 @@ import { RequestError } from '@effect/platform/HttpClientError';
|
|
|
21
22
|
|
|
22
23
|
const runtime = ManagedRuntime.make(SimpleLogLayer);
|
|
23
24
|
|
|
25
|
+
// Best-effort stringification for unknown error causes. Tries JSON first
|
|
26
|
+
// (compact, readable) and falls back to util.inspect for circular refs or
|
|
27
|
+
// values JSON can't serialize (functions, BigInt, etc.).
|
|
28
|
+
function serializeCause(cause: unknown): string {
|
|
29
|
+
try {
|
|
30
|
+
const json = JSON.stringify(cause);
|
|
31
|
+
if (json !== undefined) return json;
|
|
32
|
+
} catch {
|
|
33
|
+
// fall through
|
|
34
|
+
}
|
|
35
|
+
return inspect(cause, { depth: 2, breakLength: Infinity });
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
export const runCommandEffect = <A, E, R extends never>(
|
|
25
39
|
effect: Effect.Effect<A, E, R>,
|
|
26
40
|
): Promise<A> => runtime.runPromise(effect.pipe(printRedErrors) as any);
|
|
@@ -48,6 +62,24 @@ export const printRedErrors = Effect.catchAllCause((cause) =>
|
|
|
48
62
|
return process.exit(1);
|
|
49
63
|
}
|
|
50
64
|
|
|
65
|
+
// Surface server-side validation messages instead of dumping a stack with
|
|
66
|
+
// the useful detail buried in [cause].
|
|
67
|
+
if (theError instanceof PlatformApiError) {
|
|
68
|
+
const cause = theError.cause;
|
|
69
|
+
const causeMessage =
|
|
70
|
+
cause instanceof Error
|
|
71
|
+
? cause.message
|
|
72
|
+
: cause != null
|
|
73
|
+
? serializeCause(cause)
|
|
74
|
+
: '';
|
|
75
|
+
yield* Effect.logError(
|
|
76
|
+
causeMessage
|
|
77
|
+
? `${theError.message}: ${causeMessage}`
|
|
78
|
+
: theError.message,
|
|
79
|
+
);
|
|
80
|
+
return process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
51
83
|
// Special error handling for specific error types
|
|
52
84
|
if (theError instanceof InstantHttpError) {
|
|
53
85
|
if (theError?.message) {
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import {
|
|
3
|
+
PlatformApi as InstantPlatformApi,
|
|
4
|
+
type WebhookAction,
|
|
5
|
+
type WebhookEventInfo,
|
|
6
|
+
type WebhooksManager,
|
|
7
|
+
} from '@instantdb/platform';
|
|
8
|
+
import { AuthToken } from '../context/authToken.ts';
|
|
9
|
+
import { CurrentApp } from '../context/currentApp.ts';
|
|
10
|
+
import { PlatformApiError } from '../context/platformApi.ts';
|
|
11
|
+
import { BadArgsError } from '../errors.ts';
|
|
12
|
+
import { getBaseUrl } from './http.ts';
|
|
13
|
+
|
|
14
|
+
const getAuthedPlatformApi = Effect.gen(function* () {
|
|
15
|
+
const apiURI = yield* getBaseUrl;
|
|
16
|
+
const authToken = yield* AuthToken;
|
|
17
|
+
const token = yield* authToken.getAuthToken;
|
|
18
|
+
return new InstantPlatformApi({ apiURI, auth: { token } });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const WEBHOOK_ACTIONS: readonly WebhookAction[] = [
|
|
22
|
+
'create',
|
|
23
|
+
'update',
|
|
24
|
+
'delete',
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
export const useWebhooksManager = <R>(
|
|
28
|
+
fun: (manager: WebhooksManager<any>) => Promise<R>,
|
|
29
|
+
errorMessage?: string,
|
|
30
|
+
) =>
|
|
31
|
+
Effect.gen(function* () {
|
|
32
|
+
const api = yield* getAuthedPlatformApi;
|
|
33
|
+
const { appId } = yield* CurrentApp;
|
|
34
|
+
return yield* Effect.tryPromise({
|
|
35
|
+
try: () => fun(api.webhooks(appId).manager),
|
|
36
|
+
catch: (e) =>
|
|
37
|
+
new PlatformApiError({
|
|
38
|
+
message: errorMessage ?? 'Error using webhooks api',
|
|
39
|
+
cause: e,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Yields a `WebhooksManager` instance scoped to the current app. Use when you
|
|
46
|
+
* need to hold on to the manager outside an Effect (e.g. to call from inside
|
|
47
|
+
* an async UI callback).
|
|
48
|
+
*/
|
|
49
|
+
export const buildWebhooksManager = Effect.gen(function* () {
|
|
50
|
+
const api = yield* getAuthedPlatformApi;
|
|
51
|
+
const { appId } = yield* CurrentApp;
|
|
52
|
+
return api.webhooks(appId).manager;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Fetches the app's schema and returns the sorted list of namespace names. Returns
|
|
57
|
+
* `null` if the schema can't be fetched (network, auth, missing app, etc.) so
|
|
58
|
+
* callers can fall back to a plain text prompt.
|
|
59
|
+
*/
|
|
60
|
+
export const getRemoteNamespaces = Effect.gen(function* () {
|
|
61
|
+
const api = yield* getAuthedPlatformApi;
|
|
62
|
+
const { appId } = yield* CurrentApp;
|
|
63
|
+
const result = yield* Effect.tryPromise(() => api.getSchema(appId)).pipe(
|
|
64
|
+
Effect.orElseSucceed(() => null),
|
|
65
|
+
);
|
|
66
|
+
if (!result) return null;
|
|
67
|
+
const entities = result.schema?.entities ?? {};
|
|
68
|
+
return Object.keys(entities).sort();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Pages through `manager.listEvents` until we have `limit` events or the server
|
|
73
|
+
* runs out. Returns the events in their natural (newest-first) order.
|
|
74
|
+
*/
|
|
75
|
+
export const fetchRecentEvents = (webhookId: string, limit: number) =>
|
|
76
|
+
Effect.gen(function* () {
|
|
77
|
+
const collected: WebhookEventInfo[] = [];
|
|
78
|
+
let after: string | undefined;
|
|
79
|
+
while (collected.length < limit) {
|
|
80
|
+
const page = yield* useWebhooksManager(
|
|
81
|
+
(m) => m.listEvents(webhookId, after ? { after } : undefined),
|
|
82
|
+
'Error listing webhook events',
|
|
83
|
+
);
|
|
84
|
+
collected.push(...page.events);
|
|
85
|
+
if (!page.pageInfo.hasNextPage || !page.pageInfo.endCursor) break;
|
|
86
|
+
after = page.pageInfo.endCursor;
|
|
87
|
+
}
|
|
88
|
+
return collected.slice(0, limit);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const splitCsv = (s: string) =>
|
|
92
|
+
s
|
|
93
|
+
.split(',')
|
|
94
|
+
.map((t) => t.trim())
|
|
95
|
+
.filter((t) => t.length > 0);
|
|
96
|
+
|
|
97
|
+
export const parseNamespaces = (raw: string | undefined) =>
|
|
98
|
+
Effect.gen(function* () {
|
|
99
|
+
if (raw === undefined) return undefined;
|
|
100
|
+
const namespaces = splitCsv(raw);
|
|
101
|
+
if (namespaces.length === 0) {
|
|
102
|
+
return yield* BadArgsError.make({
|
|
103
|
+
message: '--namespaces must include at least one namespace',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return namespaces;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export const parseActions = (raw: string | undefined) =>
|
|
110
|
+
Effect.gen(function* () {
|
|
111
|
+
if (raw === undefined) return undefined;
|
|
112
|
+
const tokens = splitCsv(raw);
|
|
113
|
+
if (tokens.length === 0) {
|
|
114
|
+
return yield* BadArgsError.make({
|
|
115
|
+
message: '--actions must include at least one action',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const invalid = tokens.filter(
|
|
119
|
+
(t): t is string => !WEBHOOK_ACTIONS.includes(t as WebhookAction),
|
|
120
|
+
);
|
|
121
|
+
if (invalid.length > 0) {
|
|
122
|
+
return yield* BadArgsError.make({
|
|
123
|
+
message: `Invalid action${invalid.length === 1 ? '' : 's'}: ${invalid.join(', ')}. Must be one of: ${WEBHOOK_ACTIONS.join(', ')}`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return tokens as WebhookAction[];
|
|
127
|
+
});
|