@mediaviz/sdk 1.7.0 → 1.8.0-dev.101
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/sdk.cjs +561 -26
- package/dist/sdk.d.cts +131 -3
- package/dist/sdk.d.ts +131 -3
- package/dist/sdk.esm.d.ts +131 -3
- package/dist/sdk.esm.js +554 -27
- package/dist/sdk.umd.js +561 -26
- package/package.json +1 -1
package/dist/sdk.cjs
CHANGED
|
@@ -461,6 +461,448 @@ function requireTypes () {
|
|
|
461
461
|
return types;
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
+
var src$1;
|
|
465
|
+
var hasRequiredSrc$1;
|
|
466
|
+
|
|
467
|
+
function requireSrc$1 () {
|
|
468
|
+
if (hasRequiredSrc$1) return src$1;
|
|
469
|
+
hasRequiredSrc$1 = 1;
|
|
470
|
+
|
|
471
|
+
const { OAuthClient } = requireClient();
|
|
472
|
+
const { OAuthError } = requireErrors();
|
|
473
|
+
const { OAuthErrorCode } = requireTypes();
|
|
474
|
+
|
|
475
|
+
src$1 = { OAuthClient, OAuthError, OAuthErrorCode };
|
|
476
|
+
return src$1;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
var srcExports$1 = requireSrc$1();
|
|
480
|
+
var _oauth = /*@__PURE__*/getDefaultExportFromCjs(srcExports$1);
|
|
481
|
+
|
|
482
|
+
// Auto-generated — do not edit
|
|
483
|
+
const { OAuthClient, OAuthError, OAuthErrorCode } = _oauth;
|
|
484
|
+
|
|
485
|
+
var signing;
|
|
486
|
+
var hasRequiredSigning;
|
|
487
|
+
|
|
488
|
+
function requireSigning () {
|
|
489
|
+
if (hasRequiredSigning) return signing;
|
|
490
|
+
hasRequiredSigning = 1;
|
|
491
|
+
|
|
492
|
+
// HMAC scheme shared with the MediaViz producer: X-Signature = 'sha256=' +
|
|
493
|
+
// hex(HMAC-SHA256(secret, `${X-Timestamp}.` + rawBody)). The signing string is
|
|
494
|
+
// the timestamp joined to the *raw request body bytes* — never re-serialized
|
|
495
|
+
// JSON. Verification accepts the current and (during rotation) previous
|
|
496
|
+
// secret, rejects timestamps outside the skew window, and compares digests in
|
|
497
|
+
// constant time. Web Crypto only, so the same code runs in Node >= 18 and
|
|
498
|
+
// browsers.
|
|
499
|
+
|
|
500
|
+
const _encoder = new TextEncoder();
|
|
501
|
+
|
|
502
|
+
async function signWebhookPayload(secret, timestamp, rawBody) {
|
|
503
|
+
const key = await globalThis.crypto.subtle.importKey(
|
|
504
|
+
'raw', _encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
505
|
+
const payload = _concat(_encoder.encode(`${timestamp}.`), toBytes(rawBody));
|
|
506
|
+
const digest = await globalThis.crypto.subtle.sign('HMAC', key, payload);
|
|
507
|
+
return 'sha256=' + _hex(new Uint8Array(digest));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async function verifyWebhookSignature(secretCurrent, secretPrevious, headers, rawBody, options = {}) {
|
|
511
|
+
const h = lowerHeaders(headers);
|
|
512
|
+
const timestamp = h['x-timestamp'];
|
|
513
|
+
const signature = h['x-signature'];
|
|
514
|
+
if (!timestamp || !signature) return false;
|
|
515
|
+
if (!/^-?\d+$/.test(String(timestamp))) return false;
|
|
516
|
+
const skewToleranceS = options.skewToleranceS ?? 300;
|
|
517
|
+
const now = options.now ?? Math.floor(Date.now() / 1000);
|
|
518
|
+
if (Math.abs(now - Number(timestamp)) > skewToleranceS) return false;
|
|
519
|
+
for (const secret of [secretCurrent, secretPrevious]) {
|
|
520
|
+
if (!secret) continue;
|
|
521
|
+
const expected = await signWebhookPayload(secret, String(timestamp), rawBody);
|
|
522
|
+
if (_constantTimeEqual(expected, String(signature))) return true;
|
|
523
|
+
}
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// helpers
|
|
528
|
+
function toBytes(rawBody) {
|
|
529
|
+
if (typeof rawBody === 'string') return _encoder.encode(rawBody);
|
|
530
|
+
if (rawBody instanceof Uint8Array) return rawBody;
|
|
531
|
+
if (rawBody instanceof ArrayBuffer) return new Uint8Array(rawBody);
|
|
532
|
+
throw new TypeError('rawBody must be a string, Uint8Array, or ArrayBuffer');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function lowerHeaders(headers) {
|
|
536
|
+
const out = {};
|
|
537
|
+
const entries = typeof headers?.entries === 'function' ? headers.entries() : Object.entries(headers ?? {});
|
|
538
|
+
for (const [k, v] of entries) out[String(k).toLowerCase()] = v;
|
|
539
|
+
return out;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function _concat(a, b) {
|
|
543
|
+
const out = new Uint8Array(a.length + b.length);
|
|
544
|
+
out.set(a, 0);
|
|
545
|
+
out.set(b, a.length);
|
|
546
|
+
return out;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function _hex(bytes) {
|
|
550
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function _constantTimeEqual(a, b) {
|
|
554
|
+
if (a.length !== b.length) return false;
|
|
555
|
+
let diff = 0;
|
|
556
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
557
|
+
return diff === 0;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
signing = { signWebhookPayload, verifyWebhookSignature, toBytes, lowerHeaders };
|
|
561
|
+
return signing;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
var store;
|
|
565
|
+
var hasRequiredStore;
|
|
566
|
+
|
|
567
|
+
function requireStore () {
|
|
568
|
+
if (hasRequiredStore) return store;
|
|
569
|
+
hasRequiredStore = 1;
|
|
570
|
+
|
|
571
|
+
// Persistence seam for the webhook consumer: dedupe ledger, signing secrets,
|
|
572
|
+
// and pull cursors. The default in-memory store suits a single long-lived
|
|
573
|
+
// process (worker, dev server); multi-process consumers supply an object with
|
|
574
|
+
// the same shape backed by Redis or a database.
|
|
575
|
+
|
|
576
|
+
const MAX_SEEN_ENTRIES = 100000;
|
|
577
|
+
|
|
578
|
+
class InMemoryWebhookStore {
|
|
579
|
+
constructor() {
|
|
580
|
+
this._seen = new Map(); // eventId -> expiresAt (ms)
|
|
581
|
+
this._secrets = new Map(); // subscriptionId -> {current, previous, previousExpiresAt}
|
|
582
|
+
this._cursors = new Map(); // subscriptionId -> cursor
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
markSeen(eventId, ttlS) {
|
|
586
|
+
const now = Date.now();
|
|
587
|
+
const expiresAt = this._seen.get(eventId);
|
|
588
|
+
if (expiresAt !== undefined && expiresAt > now) return false;
|
|
589
|
+
this._seen.delete(eventId);
|
|
590
|
+
this._seen.set(eventId, now + ttlS * 1000);
|
|
591
|
+
this._prune(now);
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
unmarkSeen(eventId) {
|
|
596
|
+
this._seen.delete(eventId);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
saveSecret(subscriptionId, secret) {
|
|
600
|
+
this._secrets.set(subscriptionId, { current: secret, previous: null, previousExpiresAt: 0 });
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
rotateSecret(subscriptionId, newSecret, windowS) {
|
|
604
|
+
const entry = this._secrets.get(subscriptionId);
|
|
605
|
+
this._secrets.set(subscriptionId, {
|
|
606
|
+
current: newSecret,
|
|
607
|
+
previous: entry?.current ?? null,
|
|
608
|
+
previousExpiresAt: Date.now() + windowS * 1000,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
getSecrets(subscriptionId) {
|
|
613
|
+
const entry = this._secrets.get(subscriptionId);
|
|
614
|
+
if (!entry) return [null, null];
|
|
615
|
+
const previous = entry.previous && entry.previousExpiresAt > Date.now() ? entry.previous : null;
|
|
616
|
+
return [entry.current, previous];
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
getCursor(subscriptionId) {
|
|
620
|
+
return this._cursors.get(subscriptionId) ?? null;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
setCursor(subscriptionId, cursor) {
|
|
624
|
+
this._cursors.set(subscriptionId, cursor);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
_prune(now) {
|
|
628
|
+
if (this._seen.size <= MAX_SEEN_ENTRIES) return;
|
|
629
|
+
for (const [id, expiresAt] of this._seen) {
|
|
630
|
+
if (expiresAt <= now) this._seen.delete(id);
|
|
631
|
+
if (this._seen.size <= MAX_SEEN_ENTRIES) return;
|
|
632
|
+
}
|
|
633
|
+
// still over the cap: evict oldest insertions
|
|
634
|
+
for (const id of this._seen.keys()) {
|
|
635
|
+
this._seen.delete(id);
|
|
636
|
+
if (this._seen.size <= MAX_SEEN_ENTRIES) return;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
store = { InMemoryWebhookStore };
|
|
642
|
+
return store;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
var consumer;
|
|
646
|
+
var hasRequiredConsumer;
|
|
647
|
+
|
|
648
|
+
function requireConsumer () {
|
|
649
|
+
if (hasRequiredConsumer) return consumer;
|
|
650
|
+
hasRequiredConsumer = 1;
|
|
651
|
+
|
|
652
|
+
const { verifyWebhookSignature, toBytes, lowerHeaders } = requireSigning();
|
|
653
|
+
const { InMemoryWebhookStore } = requireStore();
|
|
654
|
+
|
|
655
|
+
// Photo columns each producer model writes — used to select a model's results
|
|
656
|
+
// out of a fetched PhotoDisplay. Mirrors the producer vocabulary; personhood is
|
|
657
|
+
// predicate-based and writes no columns.
|
|
658
|
+
const WEBHOOK_MODEL_COLUMNS = {
|
|
659
|
+
blur: ['blur_value'],
|
|
660
|
+
colors: ['main_color_palette', 'color_averages', 'hue_values', 'light_values', 'saturation_values'],
|
|
661
|
+
face_recognition: ['number_of_faces', 'bounding_boxes_from_faces_model'],
|
|
662
|
+
image_classification: ['labels_from_classifications_model', 'feature_extraction_output'],
|
|
663
|
+
image_comparison: ['average_hash'],
|
|
664
|
+
image_describe: ['topic'],
|
|
665
|
+
ocr: ['ocr_output'],
|
|
666
|
+
similarity: ['similar_photo_ids_high', 'similar_photo_ids_medium', 'similar_photo_ids_low'],
|
|
667
|
+
evidence: ['evidence_score', 'aesthetic_score', 'face_score', 'content_score', 'similarity_set_ranking'],
|
|
668
|
+
personhood: [],
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
const WEBHOOK_OUTCOME_MODELS = {
|
|
672
|
+
curation: ['blur', 'colors', 'face_recognition', 'image_classification', 'image_comparison', 'similarity', 'evidence'],
|
|
673
|
+
similarity: ['image_classification', 'image_comparison', 'similarity'],
|
|
674
|
+
persons: ['face_recognition', 'personhood'],
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
// Client-side counterpart of the MediaViz webhook producer, giving photo-by-photo
|
|
678
|
+
// progress as a project upload is analyzed.
|
|
679
|
+
//
|
|
680
|
+
// Receiver half (no auth needed): classify inbound POSTs, echo the subscription
|
|
681
|
+
// verification challenge, verify signed `target.completed` deliveries (HMAC +
|
|
682
|
+
// timestamp skew), dedupe on event_id, and dispatch each new event to a single
|
|
683
|
+
// onEvent callback. `handleRequest` is framework-agnostic glue — adapt it to
|
|
684
|
+
// express/lambda/etc. in a few lines by feeding it the raw body and headers.
|
|
685
|
+
//
|
|
686
|
+
// API half (requires an authenticated client): subscription lifecycle
|
|
687
|
+
// orchestration — register/confirm/rotate store the signing secret the moment
|
|
688
|
+
// the producer issues it — plus history pulls that share the push dedupe
|
|
689
|
+
// ledger, and out-of-band result fetches (webhooks carry a pointer, never the
|
|
690
|
+
// result itself).
|
|
691
|
+
class WebhookConsumer {
|
|
692
|
+
constructor(ctx, subscriptionApi, options = {}) {
|
|
693
|
+
this._ctx = ctx;
|
|
694
|
+
this._api = subscriptionApi;
|
|
695
|
+
this.store = options.store ?? new InMemoryWebhookStore();
|
|
696
|
+
this.skewToleranceS = options.skewToleranceS ?? 300;
|
|
697
|
+
this.rotationWindowS = options.rotationWindowS ?? 86400;
|
|
698
|
+
this.dedupeTtlS = options.dedupeTtlS ?? 604800;
|
|
699
|
+
this.pageLimit = options.pageLimit ?? 100;
|
|
700
|
+
this._onEvent = options.onEvent ?? null;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// ── receiver half ─────────────────────────────────────────────────────────
|
|
704
|
+
|
|
705
|
+
onEvent(fn) {
|
|
706
|
+
this._onEvent = fn;
|
|
707
|
+
return fn;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
useStore(store) {
|
|
711
|
+
this.store = store;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
classifyMessage(body) {
|
|
715
|
+
const type = body?.event_type;
|
|
716
|
+
if (type === 'subscription.verification') return 'verification';
|
|
717
|
+
if (type === 'target.completed') return 'delivery';
|
|
718
|
+
return 'unknown';
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
handleChallenge(body) {
|
|
722
|
+
return { challenge: body.challenge };
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
async handleDelivery(headers, rawBody) {
|
|
726
|
+
let event;
|
|
727
|
+
try {
|
|
728
|
+
event = this._parseDelivery(rawBody);
|
|
729
|
+
} catch {
|
|
730
|
+
return 'bad_signature';
|
|
731
|
+
}
|
|
732
|
+
const [current, previous] = this.store.getSecrets(String(event.subscription_id));
|
|
733
|
+
const ok = await verifyWebhookSignature(current, previous, headers, rawBody, { skewToleranceS: this.skewToleranceS });
|
|
734
|
+
if (!ok) return 'bad_signature';
|
|
735
|
+
return this._dispatch(event);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async handleRequest(headers, rawBody) {
|
|
739
|
+
let body;
|
|
740
|
+
try {
|
|
741
|
+
body = JSON.parse(_text(rawBody));
|
|
742
|
+
} catch {
|
|
743
|
+
return { status: 400, body: { detail: 'invalid JSON' } };
|
|
744
|
+
}
|
|
745
|
+
switch (this.classifyMessage(body)) {
|
|
746
|
+
case 'verification':
|
|
747
|
+
return { status: 200, body: this.handleChallenge(body) };
|
|
748
|
+
case 'delivery': {
|
|
749
|
+
const ack = await this.handleDelivery(lowerHeaders(headers), rawBody);
|
|
750
|
+
if (ack === 'bad_signature') return { status: 401, body: { detail: 'bad signature' } };
|
|
751
|
+
return { status: 200, body: { status: ack } };
|
|
752
|
+
}
|
|
753
|
+
default:
|
|
754
|
+
return { status: 400, body: { detail: 'unrecognized event_type' } };
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// ── API half ──────────────────────────────────────────────────────────────
|
|
759
|
+
|
|
760
|
+
async register(projectTableName, callbackUrl, targets) {
|
|
761
|
+
const res = await this._api.createSubscription(projectTableName, callbackUrl, targets);
|
|
762
|
+
this._saveSecretFrom(res);
|
|
763
|
+
return res;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
async confirm(subscriptionId) {
|
|
767
|
+
const res = await this._api.verifySubscription(subscriptionId);
|
|
768
|
+
this._saveSecretFrom(res);
|
|
769
|
+
return res;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
async rotateSecret(subscriptionId) {
|
|
773
|
+
const res = await this._api.rotateSubscriptionSecret(subscriptionId);
|
|
774
|
+
this.store.rotateSecret(String(subscriptionId), res.signing_secret, this.rotationWindowS);
|
|
775
|
+
return res.signing_secret;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
async updateCallback(subscriptionId, callbackUrl) {
|
|
779
|
+
// A callback change resets the subscription to pending_verification and
|
|
780
|
+
// pauses push until the new URL passes the challenge — confirm right away.
|
|
781
|
+
await this._api.updateSubscription(subscriptionId, { callbackUrl });
|
|
782
|
+
return this.confirm(subscriptionId);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
async disable(subscriptionId) {
|
|
786
|
+
return this._api.deleteSubscription(subscriptionId);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
async listSubscriptions() {
|
|
790
|
+
return this._api.listSubscriptions();
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
async pullEvents(subscriptionId, { since, limit } = {}) {
|
|
794
|
+
return this._api.listSubscriptionEvents(subscriptionId, { since, limit: limit ?? this.pageLimit });
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
async reconcile(subscriptionId, projectTableName) {
|
|
798
|
+
const id = String(subscriptionId);
|
|
799
|
+
let cursor = this.store.getCursor(id);
|
|
800
|
+
let pulled = 0;
|
|
801
|
+
let dispatched = 0;
|
|
802
|
+
for (;;) {
|
|
803
|
+
const page = await this._api.listSubscriptionEvents(id, { since: cursor ?? undefined, limit: this.pageLimit });
|
|
804
|
+
const events = page?.events ?? [];
|
|
805
|
+
pulled += events.length;
|
|
806
|
+
for (const ev of events) {
|
|
807
|
+
const ack = await this._dispatch({
|
|
808
|
+
event_id: ev.event_id,
|
|
809
|
+
event_type: 'target.completed',
|
|
810
|
+
schema_version: '1',
|
|
811
|
+
subscription_id: id,
|
|
812
|
+
project_table_name: projectTableName,
|
|
813
|
+
photo_id: ev.photo_id,
|
|
814
|
+
target: ev.target,
|
|
815
|
+
completed_at: ev.completed_at,
|
|
816
|
+
result_location: this._ctx.baseUrl + _photoPath(projectTableName, ev.photo_id),
|
|
817
|
+
});
|
|
818
|
+
if (ack === 'handled') dispatched += 1;
|
|
819
|
+
}
|
|
820
|
+
if (page?.next_cursor && page.next_cursor !== cursor) {
|
|
821
|
+
cursor = page.next_cursor;
|
|
822
|
+
this.store.setCursor(id, cursor);
|
|
823
|
+
}
|
|
824
|
+
if (!events.length || events.length < this.pageLimit) break;
|
|
825
|
+
}
|
|
826
|
+
return { pulled, dispatched };
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
async fetchResult(event) {
|
|
830
|
+
this._ctx.requireTokens();
|
|
831
|
+
const path = _photoPath(event.project_table_name, event.photo_id);
|
|
832
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
833
|
+
return { photo: data, selected: selectResultColumns(data, event.target) };
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
saveSecret(subscriptionId, secret) {
|
|
837
|
+
this.store.saveSecret(String(subscriptionId), secret);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// ── internal ──────────────────────────────────────────────────────────────
|
|
841
|
+
|
|
842
|
+
_saveSecretFrom(res) {
|
|
843
|
+
if (res?.signing_secret && res?.subscription_id != null) {
|
|
844
|
+
this.store.saveSecret(String(res.subscription_id), res.signing_secret);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
_parseDelivery(rawBody) {
|
|
849
|
+
const event = JSON.parse(_text(rawBody));
|
|
850
|
+
for (const key of ['event_id', 'subscription_id', 'target']) {
|
|
851
|
+
if (typeof event?.[key] !== 'string' || !event[key]) throw new Error(`missing ${key}`);
|
|
852
|
+
}
|
|
853
|
+
if (!Number.isInteger(event.photo_id)) throw new Error('photo_id must be an integer');
|
|
854
|
+
return event;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
async _dispatch(event) {
|
|
858
|
+
if (!this.store.markSeen(String(event.event_id), this.dedupeTtlS)) return 'duplicate';
|
|
859
|
+
if (this._onEvent) {
|
|
860
|
+
try {
|
|
861
|
+
await this._onEvent(event);
|
|
862
|
+
} catch (e) {
|
|
863
|
+
// release the dedupe claim so the producer's retry redelivers
|
|
864
|
+
this.store.unmarkSeen(String(event.event_id));
|
|
865
|
+
throw e;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return 'handled';
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// helpers
|
|
873
|
+
function selectResultColumns(photo, target) {
|
|
874
|
+
if (target === 'all') return null;
|
|
875
|
+
const sep = String(target).indexOf(':');
|
|
876
|
+
const kind = sep === -1 ? String(target) : String(target).slice(0, sep);
|
|
877
|
+
const name = sep === -1 ? '' : String(target).slice(sep + 1);
|
|
878
|
+
let columns;
|
|
879
|
+
if (kind === 'model') {
|
|
880
|
+
columns = WEBHOOK_MODEL_COLUMNS[name];
|
|
881
|
+
if (!columns) throw new Error(`unknown model '${name}'`);
|
|
882
|
+
} else if (kind === 'outcome') {
|
|
883
|
+
const models = WEBHOOK_OUTCOME_MODELS[name];
|
|
884
|
+
if (!models) throw new Error(`unknown outcome '${name}'`);
|
|
885
|
+
columns = [...new Set(models.flatMap((m) => WEBHOOK_MODEL_COLUMNS[m]))];
|
|
886
|
+
} else {
|
|
887
|
+
throw new Error(`unknown target grammar '${target}' (expected 'model:<name>', 'outcome:<name>', or 'all')`);
|
|
888
|
+
}
|
|
889
|
+
const selected = {};
|
|
890
|
+
for (const c of columns) selected[c] = photo?.[c] ?? null;
|
|
891
|
+
return selected;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
function _photoPath(projectTableName, photoId) {
|
|
895
|
+
return `/api/v1/photos/${encodeURIComponent(projectTableName)}/${encodeURIComponent(String(photoId))}`;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function _text(rawBody) {
|
|
899
|
+
return typeof rawBody === 'string' ? rawBody : new TextDecoder().decode(toBytes(rawBody));
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
consumer = { WebhookConsumer, selectResultColumns, WEBHOOK_MODEL_COLUMNS, WEBHOOK_OUTCOME_MODELS };
|
|
903
|
+
return consumer;
|
|
904
|
+
}
|
|
905
|
+
|
|
464
906
|
var src;
|
|
465
907
|
var hasRequiredSrc;
|
|
466
908
|
|
|
@@ -468,19 +910,19 @@ function requireSrc () {
|
|
|
468
910
|
if (hasRequiredSrc) return src;
|
|
469
911
|
hasRequiredSrc = 1;
|
|
470
912
|
|
|
471
|
-
const {
|
|
472
|
-
const {
|
|
473
|
-
const {
|
|
913
|
+
const { WebhookConsumer, selectResultColumns, WEBHOOK_MODEL_COLUMNS, WEBHOOK_OUTCOME_MODELS } = requireConsumer();
|
|
914
|
+
const { InMemoryWebhookStore } = requireStore();
|
|
915
|
+
const { signWebhookPayload, verifyWebhookSignature } = requireSigning();
|
|
474
916
|
|
|
475
|
-
src = {
|
|
917
|
+
src = { WebhookConsumer, InMemoryWebhookStore, signWebhookPayload, verifyWebhookSignature, selectResultColumns, WEBHOOK_MODEL_COLUMNS, WEBHOOK_OUTCOME_MODELS };
|
|
476
918
|
return src;
|
|
477
919
|
}
|
|
478
920
|
|
|
479
921
|
var srcExports = requireSrc();
|
|
480
|
-
var
|
|
922
|
+
var _webhooks = /*@__PURE__*/getDefaultExportFromCjs(srcExports);
|
|
481
923
|
|
|
482
924
|
// Auto-generated — do not edit
|
|
483
|
-
const {
|
|
925
|
+
const { WebhookConsumer, InMemoryWebhookStore, signWebhookPayload, verifyWebhookSignature, selectResultColumns, WEBHOOK_MODEL_COLUMNS, WEBHOOK_OUTCOME_MODELS } = _webhooks;
|
|
484
926
|
|
|
485
927
|
class AiModelCredits {
|
|
486
928
|
constructor(ctx) { this._ctx = ctx; }
|
|
@@ -527,7 +969,7 @@ class Company {
|
|
|
527
969
|
}
|
|
528
970
|
}
|
|
529
971
|
|
|
530
|
-
function stripUndef$
|
|
972
|
+
function stripUndef$7(o) { const r = {}; for (const k in o) if (o[k] !== undefined) r[k] = o[k]; return r; }
|
|
531
973
|
|
|
532
974
|
class CuratedAlbums {
|
|
533
975
|
constructor(ctx) { this._ctx = ctx; }
|
|
@@ -535,7 +977,7 @@ class CuratedAlbums {
|
|
|
535
977
|
async createCuratedAlbum(projectTableName, name, description = undefined, confidenceValue = undefined) {
|
|
536
978
|
this._ctx.requireTokens();
|
|
537
979
|
const path = `/api/v1/curated_album/project/${encodeURIComponent(projectTableName)}`;
|
|
538
|
-
const body = stripUndef$
|
|
980
|
+
const body = stripUndef$7({
|
|
539
981
|
name: name,
|
|
540
982
|
description: description,
|
|
541
983
|
confidence_value: confidenceValue,
|
|
@@ -589,7 +1031,7 @@ class CuratedAlbums {
|
|
|
589
1031
|
async updateCuratedAlbum(albumId, { name, description, confidenceValue } = {}) {
|
|
590
1032
|
this._ctx.requireTokens();
|
|
591
1033
|
const path = `/api/v1/curated_album/${encodeURIComponent(albumId)}`;
|
|
592
|
-
const body = stripUndef$
|
|
1034
|
+
const body = stripUndef$7({
|
|
593
1035
|
name: name,
|
|
594
1036
|
description: description,
|
|
595
1037
|
confidence_value: confidenceValue,
|
|
@@ -613,7 +1055,7 @@ class CuratedAlbums {
|
|
|
613
1055
|
}
|
|
614
1056
|
}
|
|
615
1057
|
|
|
616
|
-
function stripUndef$
|
|
1058
|
+
function stripUndef$6(o) { const r = {}; for (const k in o) if (o[k] !== undefined) r[k] = o[k]; return r; }
|
|
617
1059
|
|
|
618
1060
|
class CustomAlbums {
|
|
619
1061
|
constructor(ctx) { this._ctx = ctx; }
|
|
@@ -661,7 +1103,7 @@ class CustomAlbums {
|
|
|
661
1103
|
async createProjectCustomAlbum(projectTableName, { name, description, photoIdInclusionList, photoIdRemovalList } = {}) {
|
|
662
1104
|
this._ctx.requireTokens();
|
|
663
1105
|
const path = `/api/v1/custom_album/project/${encodeURIComponent(projectTableName)}`;
|
|
664
|
-
const body = stripUndef$
|
|
1106
|
+
const body = stripUndef$6({
|
|
665
1107
|
name: name,
|
|
666
1108
|
description: description,
|
|
667
1109
|
photo_id_inclusion_list: photoIdInclusionList,
|
|
@@ -674,7 +1116,7 @@ class CustomAlbums {
|
|
|
674
1116
|
async updateCustomAlbum(albumId, { name, description, photoIdInclusionList, photoIdRemovalList } = {}) {
|
|
675
1117
|
this._ctx.requireTokens();
|
|
676
1118
|
const path = `/api/v1/custom_album/${encodeURIComponent(albumId)}`;
|
|
677
|
-
const body = stripUndef$
|
|
1119
|
+
const body = stripUndef$6({
|
|
678
1120
|
name: name,
|
|
679
1121
|
description: description,
|
|
680
1122
|
photo_id_inclusion_list: photoIdInclusionList,
|
|
@@ -774,7 +1216,7 @@ async function handleResponse(response) {
|
|
|
774
1216
|
}
|
|
775
1217
|
}
|
|
776
1218
|
|
|
777
|
-
function stripUndef$
|
|
1219
|
+
function stripUndef$5(o) { const r = {}; for (const k in o) if (o[k] !== undefined) r[k] = o[k]; return r; }
|
|
778
1220
|
|
|
779
1221
|
class EmailTokens {
|
|
780
1222
|
constructor(ctx) { this._ctx = ctx; }
|
|
@@ -805,7 +1247,7 @@ class EmailTokens {
|
|
|
805
1247
|
}
|
|
806
1248
|
|
|
807
1249
|
async validateToken(token) {
|
|
808
|
-
const body = stripUndef$
|
|
1250
|
+
const body = stripUndef$5({
|
|
809
1251
|
token: token,
|
|
810
1252
|
});
|
|
811
1253
|
const resp = await fetch(this._ctx.baseUrl + `/api/v1/validate-token`, {
|
|
@@ -817,7 +1259,7 @@ class EmailTokens {
|
|
|
817
1259
|
}
|
|
818
1260
|
|
|
819
1261
|
async resetPassword(token, newPassword) {
|
|
820
|
-
const body = stripUndef$
|
|
1262
|
+
const body = stripUndef$5({
|
|
821
1263
|
token: token,
|
|
822
1264
|
new_password: newPassword,
|
|
823
1265
|
});
|
|
@@ -856,7 +1298,7 @@ class Health {
|
|
|
856
1298
|
}
|
|
857
1299
|
}
|
|
858
1300
|
|
|
859
|
-
function stripUndef$
|
|
1301
|
+
function stripUndef$4(o) { const r = {}; for (const k in o) if (o[k] !== undefined) r[k] = o[k]; return r; }
|
|
860
1302
|
|
|
861
1303
|
class Keywords {
|
|
862
1304
|
constructor(ctx) { this._ctx = ctx; }
|
|
@@ -864,7 +1306,7 @@ class Keywords {
|
|
|
864
1306
|
async createKeywordFilteringList(name, projectList = undefined) {
|
|
865
1307
|
this._ctx.requireTokens();
|
|
866
1308
|
const path = `/api/v1/keyword/`;
|
|
867
|
-
const body = stripUndef$
|
|
1309
|
+
const body = stripUndef$4({
|
|
868
1310
|
name: name,
|
|
869
1311
|
project_list: projectList,
|
|
870
1312
|
});
|
|
@@ -917,7 +1359,7 @@ class Keywords {
|
|
|
917
1359
|
async updateKeywordFilteringListLabels(keywordListId, listKeywordsToInclude, listKeywordsToExclude) {
|
|
918
1360
|
this._ctx.requireTokens();
|
|
919
1361
|
const path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}`;
|
|
920
|
-
const body = stripUndef$
|
|
1362
|
+
const body = stripUndef$4({
|
|
921
1363
|
list_keywords_to_include: listKeywordsToInclude,
|
|
922
1364
|
list_keywords_to_exclude: listKeywordsToExclude,
|
|
923
1365
|
});
|
|
@@ -928,7 +1370,7 @@ class Keywords {
|
|
|
928
1370
|
async updateKeywordFilteringListDetails(keywordListId, { name, projectList } = {}) {
|
|
929
1371
|
this._ctx.requireTokens();
|
|
930
1372
|
const path = `/api/v1/keyword/details/${encodeURIComponent(keywordListId)}`;
|
|
931
|
-
const body = stripUndef$
|
|
1373
|
+
const body = stripUndef$4({
|
|
932
1374
|
name: name,
|
|
933
1375
|
project_list: projectList,
|
|
934
1376
|
});
|
|
@@ -1127,6 +1569,13 @@ class Person {
|
|
|
1127
1569
|
return data;
|
|
1128
1570
|
}
|
|
1129
1571
|
|
|
1572
|
+
async removePersonName(projectTableName, personId) {
|
|
1573
|
+
this._ctx.requireTokens();
|
|
1574
|
+
const path = `/api/v1/person/${encodeURIComponent(projectTableName)}/remove_name/${encodeURIComponent(personId)}`;
|
|
1575
|
+
const { data } = await this._ctx.client.request(path, 'PUT', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1576
|
+
return data;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1130
1579
|
async combinePersons(projectTableName, destinationPersonId, oldPersonId) {
|
|
1131
1580
|
this._ctx.requireTokens();
|
|
1132
1581
|
const path = `/api/v1/person/${encodeURIComponent(projectTableName)}/combine/${encodeURIComponent(destinationPersonId)}/${encodeURIComponent(oldPersonId)}`;
|
|
@@ -1177,14 +1626,21 @@ class Person {
|
|
|
1177
1626
|
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1178
1627
|
return data;
|
|
1179
1628
|
}
|
|
1629
|
+
|
|
1630
|
+
async removePerson(projectTableName, personId) {
|
|
1631
|
+
this._ctx.requireTokens();
|
|
1632
|
+
const path = `/api/v1/person/${encodeURIComponent(projectTableName)}/${encodeURIComponent(personId)}`;
|
|
1633
|
+
const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1634
|
+
return data;
|
|
1635
|
+
}
|
|
1180
1636
|
}
|
|
1181
1637
|
|
|
1182
|
-
function stripUndef$
|
|
1638
|
+
function stripUndef$3(o) { const r = {}; for (const k in o) if (o[k] !== undefined) r[k] = o[k]; return r; }
|
|
1183
1639
|
|
|
1184
1640
|
class Projects {
|
|
1185
1641
|
constructor(ctx) { this._ctx = ctx; }
|
|
1186
1642
|
|
|
1187
|
-
async createProjectAndRun(name,
|
|
1643
|
+
async createProjectAndRun(name, type, private_ = undefined, description = undefined, directory = undefined, photoUploadVector = undefined, thumbnail = undefined, runName = undefined, { outcomes, models } = {}) {
|
|
1188
1644
|
this._ctx.requireTokens();
|
|
1189
1645
|
let path = `/api/v1/project_outcome/`;
|
|
1190
1646
|
const query = new URLSearchParams();
|
|
@@ -1192,7 +1648,7 @@ class Projects {
|
|
|
1192
1648
|
if (models !== undefined) (Array.isArray(models) ? models : [models]).forEach(v => query.append('models', v));
|
|
1193
1649
|
const qs = query.toString();
|
|
1194
1650
|
if (qs) path += '?' + qs;
|
|
1195
|
-
const body = stripUndef$
|
|
1651
|
+
const body = stripUndef$3({
|
|
1196
1652
|
name: name,
|
|
1197
1653
|
private: private_,
|
|
1198
1654
|
type: type,
|
|
@@ -1262,7 +1718,7 @@ class Projects {
|
|
|
1262
1718
|
async updateProject(projectId, { private: private_, type, description, directory, name, thumbnail } = {}) {
|
|
1263
1719
|
this._ctx.requireTokens();
|
|
1264
1720
|
const path = `/api/v1/projects/${encodeURIComponent(projectId)}`;
|
|
1265
|
-
const body = stripUndef$
|
|
1721
|
+
const body = stripUndef$3({
|
|
1266
1722
|
private: private_,
|
|
1267
1723
|
type: type,
|
|
1268
1724
|
description: description,
|
|
@@ -1341,7 +1797,7 @@ class Projects {
|
|
|
1341
1797
|
async addProjectEvent(projectTableName, event, detail = undefined) {
|
|
1342
1798
|
this._ctx.requireTokens();
|
|
1343
1799
|
const path = `/api/v1/projects/${encodeURIComponent(projectTableName)}/event`;
|
|
1344
|
-
const body = stripUndef$
|
|
1800
|
+
const body = stripUndef$3({
|
|
1345
1801
|
event: event,
|
|
1346
1802
|
detail: detail,
|
|
1347
1803
|
});
|
|
@@ -1485,7 +1941,7 @@ class Photos {
|
|
|
1485
1941
|
}
|
|
1486
1942
|
}
|
|
1487
1943
|
|
|
1488
|
-
function stripUndef$
|
|
1944
|
+
function stripUndef$2(o) { const r = {}; for (const k in o) if (o[k] !== undefined) r[k] = o[k]; return r; }
|
|
1489
1945
|
|
|
1490
1946
|
class Search {
|
|
1491
1947
|
constructor(ctx) { this._ctx = ctx; }
|
|
@@ -1567,7 +2023,7 @@ class Search {
|
|
|
1567
2023
|
if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
|
|
1568
2024
|
const qs = query.toString();
|
|
1569
2025
|
if (qs) path += '?' + qs;
|
|
1570
|
-
const body = stripUndef$
|
|
2026
|
+
const body = stripUndef$2({
|
|
1571
2027
|
search_text: searchText,
|
|
1572
2028
|
size: size,
|
|
1573
2029
|
});
|
|
@@ -1623,6 +2079,75 @@ class Search {
|
|
|
1623
2079
|
}
|
|
1624
2080
|
}
|
|
1625
2081
|
|
|
2082
|
+
function stripUndef$1(o) { const r = {}; for (const k in o) if (o[k] !== undefined) r[k] = o[k]; return r; }
|
|
2083
|
+
|
|
2084
|
+
class Subscription {
|
|
2085
|
+
constructor(ctx) { this._ctx = ctx; }
|
|
2086
|
+
|
|
2087
|
+
async createSubscription(projectTableName, callbackUrl, targets) {
|
|
2088
|
+
this._ctx.requireTokens();
|
|
2089
|
+
const path = `/api/v1/subscriptions`;
|
|
2090
|
+
const body = stripUndef$1({
|
|
2091
|
+
project_table_name: projectTableName,
|
|
2092
|
+
callback_url: callbackUrl,
|
|
2093
|
+
targets: targets,
|
|
2094
|
+
});
|
|
2095
|
+
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
|
|
2096
|
+
return data;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
async verifySubscription(subscriptionId) {
|
|
2100
|
+
this._ctx.requireTokens();
|
|
2101
|
+
const path = `/api/v1/subscriptions/${encodeURIComponent(subscriptionId)}/verify`;
|
|
2102
|
+
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken);
|
|
2103
|
+
return data;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
async listSubscriptions() {
|
|
2107
|
+
this._ctx.requireTokens();
|
|
2108
|
+
const path = `/api/v1/subscriptions`;
|
|
2109
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
2110
|
+
return data;
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
async listSubscriptionEvents(subscriptionId, { since, limit } = {}) {
|
|
2114
|
+
this._ctx.requireTokens();
|
|
2115
|
+
let path = `/api/v1/subscriptions/${encodeURIComponent(subscriptionId)}/events`;
|
|
2116
|
+
const query = new URLSearchParams();
|
|
2117
|
+
if (since !== undefined) (Array.isArray(since) ? since : [since]).forEach(v => query.append('since', v));
|
|
2118
|
+
if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
|
|
2119
|
+
const qs = query.toString();
|
|
2120
|
+
if (qs) path += '?' + qs;
|
|
2121
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
2122
|
+
return data;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
async updateSubscription(subscriptionId, { callbackUrl, targets } = {}) {
|
|
2126
|
+
this._ctx.requireTokens();
|
|
2127
|
+
const path = `/api/v1/subscriptions/${encodeURIComponent(subscriptionId)}`;
|
|
2128
|
+
const body = stripUndef$1({
|
|
2129
|
+
callback_url: callbackUrl,
|
|
2130
|
+
targets: targets,
|
|
2131
|
+
});
|
|
2132
|
+
const { data } = await this._ctx.client.request(path, 'PATCH', this._ctx.accessToken, this._ctx.refreshToken, body);
|
|
2133
|
+
return data;
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
async deleteSubscription(subscriptionId) {
|
|
2137
|
+
this._ctx.requireTokens();
|
|
2138
|
+
const path = `/api/v1/subscriptions/${encodeURIComponent(subscriptionId)}`;
|
|
2139
|
+
const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
|
|
2140
|
+
return data;
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
async rotateSubscriptionSecret(subscriptionId) {
|
|
2144
|
+
this._ctx.requireTokens();
|
|
2145
|
+
const path = `/api/v1/subscriptions/${encodeURIComponent(subscriptionId)}/rotate_secret`;
|
|
2146
|
+
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken);
|
|
2147
|
+
return data;
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
|
|
1626
2151
|
function stripUndef(o) { const r = {}; for (const k in o) if (o[k] !== undefined) r[k] = o[k]; return r; }
|
|
1627
2152
|
|
|
1628
2153
|
class Users {
|
|
@@ -1806,7 +2331,9 @@ class MediaViz {
|
|
|
1806
2331
|
this.photos = new Photos(_ctx);
|
|
1807
2332
|
this.projects = new Projects(_ctx);
|
|
1808
2333
|
this.search = new Search(_ctx);
|
|
2334
|
+
this.subscription = new Subscription(_ctx);
|
|
1809
2335
|
this.users = new Users(_ctx);
|
|
2336
|
+
this.webhooks = new WebhookConsumer(_ctx, this.subscription);
|
|
1810
2337
|
}
|
|
1811
2338
|
|
|
1812
2339
|
async authenticate() {
|
|
@@ -1844,6 +2371,7 @@ exports.CuratedAlbums = CuratedAlbums;
|
|
|
1844
2371
|
exports.CustomAlbums = CustomAlbums;
|
|
1845
2372
|
exports.EmailTokens = EmailTokens;
|
|
1846
2373
|
exports.Health = Health;
|
|
2374
|
+
exports.InMemoryWebhookStore = InMemoryWebhookStore;
|
|
1847
2375
|
exports.Keywords = Keywords;
|
|
1848
2376
|
exports.MediaViz = MediaViz;
|
|
1849
2377
|
exports.NotFoundError = NotFoundError;
|
|
@@ -1860,6 +2388,13 @@ exports.Projects = Projects;
|
|
|
1860
2388
|
exports.RateLimitError = RateLimitError;
|
|
1861
2389
|
exports.Search = Search;
|
|
1862
2390
|
exports.ServerError = ServerError;
|
|
2391
|
+
exports.Subscription = Subscription;
|
|
1863
2392
|
exports.Users = Users;
|
|
1864
2393
|
exports.ValidationError = ValidationError;
|
|
2394
|
+
exports.WEBHOOK_MODEL_COLUMNS = WEBHOOK_MODEL_COLUMNS;
|
|
2395
|
+
exports.WEBHOOK_OUTCOME_MODELS = WEBHOOK_OUTCOME_MODELS;
|
|
2396
|
+
exports.WebhookConsumer = WebhookConsumer;
|
|
1865
2397
|
exports.handleResponse = handleResponse;
|
|
2398
|
+
exports.selectResultColumns = selectResultColumns;
|
|
2399
|
+
exports.signWebhookPayload = signWebhookPayload;
|
|
2400
|
+
exports.verifyWebhookSignature = verifyWebhookSignature;
|