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