@innspel/core 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -1
- package/dist/index.cjs +193 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -2
- package/dist/index.d.ts +152 -2
- package/dist/index.js +186 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -150,6 +150,24 @@ type ProgramsTaskCompletionView = {
|
|
|
150
150
|
* Utfallet av én oppgave, valgt av testeren. Tre lukkede valg, aldri skala (rapport 4).
|
|
151
151
|
*/
|
|
152
152
|
type ProgramsTaskOutcome = 'fungerte' | 'delvis' | 'ikke';
|
|
153
|
+
/**
|
|
154
|
+
* Tillatte bildetyper. Aldri SVG eller GIF (K9). Sjekkes på magic bytes ved mottak, ikke bare på oppgitt verdi.
|
|
155
|
+
*/
|
|
156
|
+
type SubmissionsAttachmentMime = 'image/png' | 'image/jpeg' | 'image/webp';
|
|
157
|
+
/**
|
|
158
|
+
* Vedleggets tilstand etter complete. Verifiseringen (magic bytes, dimensjon, re-enkoding, EXIF-strip) kjører asynkront i isolert prosess; klienten trenger ikke vente.
|
|
159
|
+
*/
|
|
160
|
+
type SubmissionsAttachmentView = {
|
|
161
|
+
id: string;
|
|
162
|
+
submissionId: string;
|
|
163
|
+
mime: SubmissionsAttachmentMime;
|
|
164
|
+
bytes: number;
|
|
165
|
+
/**
|
|
166
|
+
* Satt når vedlegget er verifisert og re-enkodet. Utelatt = under behandling.
|
|
167
|
+
*/
|
|
168
|
+
processedAt?: string;
|
|
169
|
+
createdAt: string;
|
|
170
|
+
};
|
|
153
171
|
/**
|
|
154
172
|
* Minimal peker til en kjent feil, for visning i innsenderens liste.
|
|
155
173
|
*/
|
|
@@ -341,9 +359,47 @@ interface ResolvedSettings {
|
|
|
341
359
|
minTextLength: number;
|
|
342
360
|
screenshotsEnabled: boolean;
|
|
343
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Bildet slik verten gir det fra seg — Blob (React Native og nettleser),
|
|
364
|
+
* ArrayBuffer eller en typed array. Kjernen leser bare lengden; innholdet går
|
|
365
|
+
* rett til lagringen.
|
|
366
|
+
*/
|
|
367
|
+
type AttachmentBody = Blob | ArrayBuffer | ArrayBufferView<ArrayBuffer>;
|
|
368
|
+
/**
|
|
369
|
+
* Ett skjermbilde, MASKERT på klienten før dette (K4, DD-39, DD-40). Kjernen
|
|
370
|
+
* sjekker grensene (PNG/JPEG/WebP, ≤ 5 MB, ≤ 4096², K9) og kaster med feltnavn
|
|
371
|
+
* før noe går på nettet; plattformen sjekker dem igjen før dekoding.
|
|
372
|
+
*/
|
|
373
|
+
interface AttachmentInput {
|
|
374
|
+
mime: SubmissionsAttachmentMime;
|
|
375
|
+
body: AttachmentBody;
|
|
376
|
+
width: number;
|
|
377
|
+
height: number;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Hva som skjedde med vedlegget etter at teksten var lagret (DD-42).
|
|
381
|
+
* `failed` er ikke en feil for innsendingen — saken står, uten bilde — men
|
|
382
|
+
* UI-et skal si det: «Sendt — skjermbildet kunne ikke legges ved».
|
|
383
|
+
*/
|
|
384
|
+
type AttachmentOutcome = {
|
|
385
|
+
status: 'uploaded';
|
|
386
|
+
attachment: SubmissionsAttachmentView;
|
|
387
|
+
} | {
|
|
388
|
+
status: 'failed';
|
|
389
|
+
error: Error;
|
|
390
|
+
}
|
|
391
|
+
/** Teksten ble køet offline; et skjermbilde køes aldri (DD-41), så det ble forkastet. */
|
|
392
|
+
| {
|
|
393
|
+
status: 'dropped';
|
|
394
|
+
};
|
|
344
395
|
/** Felles felter for begge spor. */
|
|
345
396
|
interface CaptureBase {
|
|
346
397
|
text: string;
|
|
398
|
+
/**
|
|
399
|
+
* Skjermbilde, maskert på klienten. Sendes ETTER teksten, i egen forespørsel,
|
|
400
|
+
* og kan feile alene (DD-42) — se `CaptureResult.attachment`.
|
|
401
|
+
*/
|
|
402
|
+
attachment?: AttachmentInput;
|
|
347
403
|
/** Legges på konteksten fra `init` — typisk `screen`. */
|
|
348
404
|
context?: Partial<ContextInput>;
|
|
349
405
|
/**
|
|
@@ -371,9 +427,13 @@ type CaptureInput = CaptureFeil | CaptureOnske;
|
|
|
371
427
|
type CaptureResult = {
|
|
372
428
|
status: 'sent';
|
|
373
429
|
submission: SubmissionsSubmissionStatusView;
|
|
430
|
+
attachment?: AttachmentOutcome;
|
|
374
431
|
} | {
|
|
375
432
|
status: 'queued';
|
|
376
433
|
clientRef: string;
|
|
434
|
+
attachment?: {
|
|
435
|
+
status: 'dropped';
|
|
436
|
+
};
|
|
377
437
|
};
|
|
378
438
|
/** En rad i offline-køen. Kun tekst — et skjermbilde køes aldri (rapport 3, kap. 5). */
|
|
379
439
|
interface QueuedSubmission {
|
|
@@ -457,6 +517,16 @@ interface InnspelClient {
|
|
|
457
517
|
readonly programs: {
|
|
458
518
|
list(): Promise<ProgramsMyPrograms>;
|
|
459
519
|
};
|
|
520
|
+
readonly attachments: {
|
|
521
|
+
/**
|
|
522
|
+
* Laster opp ett maskert skjermbilde til en innsending som alt er lagret:
|
|
523
|
+
* presign → PUT til lagringen → complete. Bruk `capture({ attachment })` i
|
|
524
|
+
* stedet når teksten og bildet sendes sammen — da håndteres DD-42 for deg.
|
|
525
|
+
* Plattformen verifiserer og re-enkoder etterpå; `processedAt` er satt når
|
|
526
|
+
* det er gjort.
|
|
527
|
+
*/
|
|
528
|
+
upload(submissionId: string, attachment: AttachmentInput): Promise<SubmissionsAttachmentView>;
|
|
529
|
+
};
|
|
460
530
|
readonly queue: {
|
|
461
531
|
pending(): Promise<QueuedSubmission[]>;
|
|
462
532
|
/** Sendes ellers automatisk når widgeten åpnes. Aldri ved oppstart (DD-41). */
|
|
@@ -565,6 +635,86 @@ declare class InnspelQueueFullError extends Error {
|
|
|
565
635
|
*/
|
|
566
636
|
declare const KONTEKST_FELTER: readonly ["platform", "appVersion", "build", "os", "screen", "locale"];
|
|
567
637
|
|
|
638
|
+
/**
|
|
639
|
+
* Grensene for et vedlegg (K9). Tallene er kontraktens
|
|
640
|
+
* (`AttachmentPresignRequest`: 5 MB, 4096²) og sjekkes her FØR noe går på
|
|
641
|
+
* nettet — plattformen sjekker dem på nytt før dekoding.
|
|
642
|
+
*/
|
|
643
|
+
declare const VEDLEGG_GRENSER: {
|
|
644
|
+
readonly maxBytes: 5242880;
|
|
645
|
+
readonly maxDimension: 4096;
|
|
646
|
+
};
|
|
647
|
+
/** Allowlisten (K9): aldri SVG, aldri GIF. Avgjøres på ledningen av magic bytes, ikke av denne strengen. */
|
|
648
|
+
declare const TILLATTE_MIME: readonly SubmissionsAttachmentMime[];
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Geometrien bak maskeringen (DD-39, DD-40) — ren TypeScript uten UI, så den
|
|
652
|
+
* kan testes her og brukes likt av `@innspel/react-native` og `@innspel/web`.
|
|
653
|
+
*
|
|
654
|
+
* Alt er rektangler i BILDETS pikselkoordinater. UI-laget tegner dem som
|
|
655
|
+
* heldekkende flater over forhåndsvisningen og rekomponerer bitmapen; det som
|
|
656
|
+
* står her avgjør bare HVOR flatene ligger. Private views (DD-39) er rektangler
|
|
657
|
+
* som UI-et aldri lar brukeren fjerne — den regelen håndheves der, ikke her,
|
|
658
|
+
* fordi den handler om hvem som la rektangelet, ikke om geometri.
|
|
659
|
+
*/
|
|
660
|
+
interface MaskPoint {
|
|
661
|
+
x: number;
|
|
662
|
+
y: number;
|
|
663
|
+
}
|
|
664
|
+
interface MaskRect {
|
|
665
|
+
x: number;
|
|
666
|
+
y: number;
|
|
667
|
+
width: number;
|
|
668
|
+
height: number;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Ett fingerstrøk («Tegn over», DD-40) til rektangler: en kvadratisk flate per
|
|
672
|
+
* punkt, og én per mellomrom mellom to punkter, så strøket er tett selv om
|
|
673
|
+
* fingeren beveget seg fort. Alle klippes til bildet.
|
|
674
|
+
*/
|
|
675
|
+
declare function strokTilRekter(punkter: readonly MaskPoint[], penselBredde: number, bilde: {
|
|
676
|
+
width: number;
|
|
677
|
+
height: number;
|
|
678
|
+
}): MaskRect[];
|
|
679
|
+
/** «Masker felt» (DD-40): ett trykk legger en boks sentrert på punktet. */
|
|
680
|
+
declare function trykkTilRekt(punkt: MaskPoint, boks: {
|
|
681
|
+
width: number;
|
|
682
|
+
height: number;
|
|
683
|
+
}, bilde: {
|
|
684
|
+
width: number;
|
|
685
|
+
height: number;
|
|
686
|
+
}): MaskRect | null;
|
|
687
|
+
/**
|
|
688
|
+
* Klipper et rektangel til bildet. Null når ingenting er igjen — et rektangel
|
|
689
|
+
* helt utenfor bildet skal ikke tegnes, og heller ikke telles som en maskering.
|
|
690
|
+
*/
|
|
691
|
+
declare function klipp(r: MaskRect, bilde: {
|
|
692
|
+
width: number;
|
|
693
|
+
height: number;
|
|
694
|
+
}): MaskRect | null;
|
|
695
|
+
/**
|
|
696
|
+
* Skalerer et rektangel fra ett koordinatsystem til et annet — typisk fra
|
|
697
|
+
* skjermpunkter (der fingeren var) til bildepiksler (der masken skal ligge).
|
|
698
|
+
* Rundes UTOVER: en maske skal aldri bli en piksel for liten.
|
|
699
|
+
*/
|
|
700
|
+
declare function skaler(r: MaskRect, fra: {
|
|
701
|
+
width: number;
|
|
702
|
+
height: number;
|
|
703
|
+
}, til: {
|
|
704
|
+
width: number;
|
|
705
|
+
height: number;
|
|
706
|
+
}): MaskRect;
|
|
707
|
+
/** Om et punkt ligger i et av rektanglene — brukes for «trykk på en maske for å angre den». */
|
|
708
|
+
declare function treffer(punkt: MaskPoint, r: MaskRect): boolean;
|
|
709
|
+
/**
|
|
710
|
+
* Om rektanglene dekker hele bildet. UI-et bruker det til å si «alt er
|
|
711
|
+
* maskert» — da er det ingenting å sende, og «Fjern bildet» er det ærlige valget.
|
|
712
|
+
*/
|
|
713
|
+
declare function dekkerAlt(rekter: readonly MaskRect[], bilde: {
|
|
714
|
+
width: number;
|
|
715
|
+
height: number;
|
|
716
|
+
}): boolean;
|
|
717
|
+
|
|
568
718
|
/**
|
|
569
719
|
* Versjonen som sendes som `context.sdkVersion` på hver innsending.
|
|
570
720
|
*
|
|
@@ -574,6 +724,6 @@ declare const KONTEKST_FELTER: readonly ["platform", "appVersion", "build", "os"
|
|
|
574
724
|
* ikke kan lese JSON, og `test/versjon.test.ts` holder den i takt med
|
|
575
725
|
* `package.json`.
|
|
576
726
|
*/
|
|
577
|
-
declare const VERSJON = "0.
|
|
727
|
+
declare const VERSJON = "0.2.0";
|
|
578
728
|
|
|
579
|
-
export { type CaptureFeil, type CaptureInput, type CaptureOnske, type CaptureResult, type ContextInput, type CommonCorrelation as Correlation, type CommonErrorCode as ErrorCode, type FlushResult, type IdentityProvider, InnspelApiError, type InnspelClient, InnspelConfigError, type InnspelOptions, InnspelQueueFullError, type InnspelStorage, KONTEKST_FELTER, KO_TAK, type KnownIssuesKnownIssueCard as KnownIssueCard, type KnownIssueQuery, type SubmissionsKnownIssueRef as KnownIssueRef, type KnownIssuesKnownIssueStatus as KnownIssueStatus, type ProgramsMyPrograms as MyPrograms, type MySubmission, type OpenFeedbackOptions, type PatternFinding, type PatternKind, type CommonPlatform as Platform, type Presenter, type QueuedSubmission, type SubmissionsReasonCode as ReasonCode, type SubmissionsReplyView as ReplyView, type ResolvedSettings, type SubmissionsStatusType as StatusType, type SubmissionsSubmissionStatus as SubmissionStatus, type SubmissionsSubmissionStatusView as SubmissionStatusView, type SubmissionsSubmissionType as SubmissionType, type Telemetry, type TelemetryEvent, type TextMaxLength, VERSJON, init, scanText };
|
|
729
|
+
export { type AttachmentBody, type AttachmentInput, type SubmissionsAttachmentMime as AttachmentMime, type AttachmentOutcome, type SubmissionsAttachmentView as AttachmentView, type CaptureFeil, type CaptureInput, type CaptureOnske, type CaptureResult, type ContextInput, type CommonCorrelation as Correlation, type CommonErrorCode as ErrorCode, type FlushResult, type IdentityProvider, InnspelApiError, type InnspelClient, InnspelConfigError, type InnspelOptions, InnspelQueueFullError, type InnspelStorage, KONTEKST_FELTER, KO_TAK, type KnownIssuesKnownIssueCard as KnownIssueCard, type KnownIssueQuery, type SubmissionsKnownIssueRef as KnownIssueRef, type KnownIssuesKnownIssueStatus as KnownIssueStatus, type MaskPoint, type MaskRect, type ProgramsMyPrograms as MyPrograms, type MySubmission, type OpenFeedbackOptions, type PatternFinding, type PatternKind, type CommonPlatform as Platform, type Presenter, type QueuedSubmission, type SubmissionsReasonCode as ReasonCode, type SubmissionsReplyView as ReplyView, type ResolvedSettings, type SubmissionsStatusType as StatusType, type SubmissionsSubmissionStatus as SubmissionStatus, type SubmissionsSubmissionStatusView as SubmissionStatusView, type SubmissionsSubmissionType as SubmissionType, TILLATTE_MIME, type Telemetry, type TelemetryEvent, type TextMaxLength, VEDLEGG_GRENSER, VERSJON, dekkerAlt, init, klipp, scanText, skaler, strokTilRekter, treffer, trykkTilRekt };
|
package/dist/index.d.ts
CHANGED
|
@@ -150,6 +150,24 @@ type ProgramsTaskCompletionView = {
|
|
|
150
150
|
* Utfallet av én oppgave, valgt av testeren. Tre lukkede valg, aldri skala (rapport 4).
|
|
151
151
|
*/
|
|
152
152
|
type ProgramsTaskOutcome = 'fungerte' | 'delvis' | 'ikke';
|
|
153
|
+
/**
|
|
154
|
+
* Tillatte bildetyper. Aldri SVG eller GIF (K9). Sjekkes på magic bytes ved mottak, ikke bare på oppgitt verdi.
|
|
155
|
+
*/
|
|
156
|
+
type SubmissionsAttachmentMime = 'image/png' | 'image/jpeg' | 'image/webp';
|
|
157
|
+
/**
|
|
158
|
+
* Vedleggets tilstand etter complete. Verifiseringen (magic bytes, dimensjon, re-enkoding, EXIF-strip) kjører asynkront i isolert prosess; klienten trenger ikke vente.
|
|
159
|
+
*/
|
|
160
|
+
type SubmissionsAttachmentView = {
|
|
161
|
+
id: string;
|
|
162
|
+
submissionId: string;
|
|
163
|
+
mime: SubmissionsAttachmentMime;
|
|
164
|
+
bytes: number;
|
|
165
|
+
/**
|
|
166
|
+
* Satt når vedlegget er verifisert og re-enkodet. Utelatt = under behandling.
|
|
167
|
+
*/
|
|
168
|
+
processedAt?: string;
|
|
169
|
+
createdAt: string;
|
|
170
|
+
};
|
|
153
171
|
/**
|
|
154
172
|
* Minimal peker til en kjent feil, for visning i innsenderens liste.
|
|
155
173
|
*/
|
|
@@ -341,9 +359,47 @@ interface ResolvedSettings {
|
|
|
341
359
|
minTextLength: number;
|
|
342
360
|
screenshotsEnabled: boolean;
|
|
343
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Bildet slik verten gir det fra seg — Blob (React Native og nettleser),
|
|
364
|
+
* ArrayBuffer eller en typed array. Kjernen leser bare lengden; innholdet går
|
|
365
|
+
* rett til lagringen.
|
|
366
|
+
*/
|
|
367
|
+
type AttachmentBody = Blob | ArrayBuffer | ArrayBufferView<ArrayBuffer>;
|
|
368
|
+
/**
|
|
369
|
+
* Ett skjermbilde, MASKERT på klienten før dette (K4, DD-39, DD-40). Kjernen
|
|
370
|
+
* sjekker grensene (PNG/JPEG/WebP, ≤ 5 MB, ≤ 4096², K9) og kaster med feltnavn
|
|
371
|
+
* før noe går på nettet; plattformen sjekker dem igjen før dekoding.
|
|
372
|
+
*/
|
|
373
|
+
interface AttachmentInput {
|
|
374
|
+
mime: SubmissionsAttachmentMime;
|
|
375
|
+
body: AttachmentBody;
|
|
376
|
+
width: number;
|
|
377
|
+
height: number;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Hva som skjedde med vedlegget etter at teksten var lagret (DD-42).
|
|
381
|
+
* `failed` er ikke en feil for innsendingen — saken står, uten bilde — men
|
|
382
|
+
* UI-et skal si det: «Sendt — skjermbildet kunne ikke legges ved».
|
|
383
|
+
*/
|
|
384
|
+
type AttachmentOutcome = {
|
|
385
|
+
status: 'uploaded';
|
|
386
|
+
attachment: SubmissionsAttachmentView;
|
|
387
|
+
} | {
|
|
388
|
+
status: 'failed';
|
|
389
|
+
error: Error;
|
|
390
|
+
}
|
|
391
|
+
/** Teksten ble køet offline; et skjermbilde køes aldri (DD-41), så det ble forkastet. */
|
|
392
|
+
| {
|
|
393
|
+
status: 'dropped';
|
|
394
|
+
};
|
|
344
395
|
/** Felles felter for begge spor. */
|
|
345
396
|
interface CaptureBase {
|
|
346
397
|
text: string;
|
|
398
|
+
/**
|
|
399
|
+
* Skjermbilde, maskert på klienten. Sendes ETTER teksten, i egen forespørsel,
|
|
400
|
+
* og kan feile alene (DD-42) — se `CaptureResult.attachment`.
|
|
401
|
+
*/
|
|
402
|
+
attachment?: AttachmentInput;
|
|
347
403
|
/** Legges på konteksten fra `init` — typisk `screen`. */
|
|
348
404
|
context?: Partial<ContextInput>;
|
|
349
405
|
/**
|
|
@@ -371,9 +427,13 @@ type CaptureInput = CaptureFeil | CaptureOnske;
|
|
|
371
427
|
type CaptureResult = {
|
|
372
428
|
status: 'sent';
|
|
373
429
|
submission: SubmissionsSubmissionStatusView;
|
|
430
|
+
attachment?: AttachmentOutcome;
|
|
374
431
|
} | {
|
|
375
432
|
status: 'queued';
|
|
376
433
|
clientRef: string;
|
|
434
|
+
attachment?: {
|
|
435
|
+
status: 'dropped';
|
|
436
|
+
};
|
|
377
437
|
};
|
|
378
438
|
/** En rad i offline-køen. Kun tekst — et skjermbilde køes aldri (rapport 3, kap. 5). */
|
|
379
439
|
interface QueuedSubmission {
|
|
@@ -457,6 +517,16 @@ interface InnspelClient {
|
|
|
457
517
|
readonly programs: {
|
|
458
518
|
list(): Promise<ProgramsMyPrograms>;
|
|
459
519
|
};
|
|
520
|
+
readonly attachments: {
|
|
521
|
+
/**
|
|
522
|
+
* Laster opp ett maskert skjermbilde til en innsending som alt er lagret:
|
|
523
|
+
* presign → PUT til lagringen → complete. Bruk `capture({ attachment })` i
|
|
524
|
+
* stedet når teksten og bildet sendes sammen — da håndteres DD-42 for deg.
|
|
525
|
+
* Plattformen verifiserer og re-enkoder etterpå; `processedAt` er satt når
|
|
526
|
+
* det er gjort.
|
|
527
|
+
*/
|
|
528
|
+
upload(submissionId: string, attachment: AttachmentInput): Promise<SubmissionsAttachmentView>;
|
|
529
|
+
};
|
|
460
530
|
readonly queue: {
|
|
461
531
|
pending(): Promise<QueuedSubmission[]>;
|
|
462
532
|
/** Sendes ellers automatisk når widgeten åpnes. Aldri ved oppstart (DD-41). */
|
|
@@ -565,6 +635,86 @@ declare class InnspelQueueFullError extends Error {
|
|
|
565
635
|
*/
|
|
566
636
|
declare const KONTEKST_FELTER: readonly ["platform", "appVersion", "build", "os", "screen", "locale"];
|
|
567
637
|
|
|
638
|
+
/**
|
|
639
|
+
* Grensene for et vedlegg (K9). Tallene er kontraktens
|
|
640
|
+
* (`AttachmentPresignRequest`: 5 MB, 4096²) og sjekkes her FØR noe går på
|
|
641
|
+
* nettet — plattformen sjekker dem på nytt før dekoding.
|
|
642
|
+
*/
|
|
643
|
+
declare const VEDLEGG_GRENSER: {
|
|
644
|
+
readonly maxBytes: 5242880;
|
|
645
|
+
readonly maxDimension: 4096;
|
|
646
|
+
};
|
|
647
|
+
/** Allowlisten (K9): aldri SVG, aldri GIF. Avgjøres på ledningen av magic bytes, ikke av denne strengen. */
|
|
648
|
+
declare const TILLATTE_MIME: readonly SubmissionsAttachmentMime[];
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Geometrien bak maskeringen (DD-39, DD-40) — ren TypeScript uten UI, så den
|
|
652
|
+
* kan testes her og brukes likt av `@innspel/react-native` og `@innspel/web`.
|
|
653
|
+
*
|
|
654
|
+
* Alt er rektangler i BILDETS pikselkoordinater. UI-laget tegner dem som
|
|
655
|
+
* heldekkende flater over forhåndsvisningen og rekomponerer bitmapen; det som
|
|
656
|
+
* står her avgjør bare HVOR flatene ligger. Private views (DD-39) er rektangler
|
|
657
|
+
* som UI-et aldri lar brukeren fjerne — den regelen håndheves der, ikke her,
|
|
658
|
+
* fordi den handler om hvem som la rektangelet, ikke om geometri.
|
|
659
|
+
*/
|
|
660
|
+
interface MaskPoint {
|
|
661
|
+
x: number;
|
|
662
|
+
y: number;
|
|
663
|
+
}
|
|
664
|
+
interface MaskRect {
|
|
665
|
+
x: number;
|
|
666
|
+
y: number;
|
|
667
|
+
width: number;
|
|
668
|
+
height: number;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Ett fingerstrøk («Tegn over», DD-40) til rektangler: en kvadratisk flate per
|
|
672
|
+
* punkt, og én per mellomrom mellom to punkter, så strøket er tett selv om
|
|
673
|
+
* fingeren beveget seg fort. Alle klippes til bildet.
|
|
674
|
+
*/
|
|
675
|
+
declare function strokTilRekter(punkter: readonly MaskPoint[], penselBredde: number, bilde: {
|
|
676
|
+
width: number;
|
|
677
|
+
height: number;
|
|
678
|
+
}): MaskRect[];
|
|
679
|
+
/** «Masker felt» (DD-40): ett trykk legger en boks sentrert på punktet. */
|
|
680
|
+
declare function trykkTilRekt(punkt: MaskPoint, boks: {
|
|
681
|
+
width: number;
|
|
682
|
+
height: number;
|
|
683
|
+
}, bilde: {
|
|
684
|
+
width: number;
|
|
685
|
+
height: number;
|
|
686
|
+
}): MaskRect | null;
|
|
687
|
+
/**
|
|
688
|
+
* Klipper et rektangel til bildet. Null når ingenting er igjen — et rektangel
|
|
689
|
+
* helt utenfor bildet skal ikke tegnes, og heller ikke telles som en maskering.
|
|
690
|
+
*/
|
|
691
|
+
declare function klipp(r: MaskRect, bilde: {
|
|
692
|
+
width: number;
|
|
693
|
+
height: number;
|
|
694
|
+
}): MaskRect | null;
|
|
695
|
+
/**
|
|
696
|
+
* Skalerer et rektangel fra ett koordinatsystem til et annet — typisk fra
|
|
697
|
+
* skjermpunkter (der fingeren var) til bildepiksler (der masken skal ligge).
|
|
698
|
+
* Rundes UTOVER: en maske skal aldri bli en piksel for liten.
|
|
699
|
+
*/
|
|
700
|
+
declare function skaler(r: MaskRect, fra: {
|
|
701
|
+
width: number;
|
|
702
|
+
height: number;
|
|
703
|
+
}, til: {
|
|
704
|
+
width: number;
|
|
705
|
+
height: number;
|
|
706
|
+
}): MaskRect;
|
|
707
|
+
/** Om et punkt ligger i et av rektanglene — brukes for «trykk på en maske for å angre den». */
|
|
708
|
+
declare function treffer(punkt: MaskPoint, r: MaskRect): boolean;
|
|
709
|
+
/**
|
|
710
|
+
* Om rektanglene dekker hele bildet. UI-et bruker det til å si «alt er
|
|
711
|
+
* maskert» — da er det ingenting å sende, og «Fjern bildet» er det ærlige valget.
|
|
712
|
+
*/
|
|
713
|
+
declare function dekkerAlt(rekter: readonly MaskRect[], bilde: {
|
|
714
|
+
width: number;
|
|
715
|
+
height: number;
|
|
716
|
+
}): boolean;
|
|
717
|
+
|
|
568
718
|
/**
|
|
569
719
|
* Versjonen som sendes som `context.sdkVersion` på hver innsending.
|
|
570
720
|
*
|
|
@@ -574,6 +724,6 @@ declare const KONTEKST_FELTER: readonly ["platform", "appVersion", "build", "os"
|
|
|
574
724
|
* ikke kan lese JSON, og `test/versjon.test.ts` holder den i takt med
|
|
575
725
|
* `package.json`.
|
|
576
726
|
*/
|
|
577
|
-
declare const VERSJON = "0.
|
|
727
|
+
declare const VERSJON = "0.2.0";
|
|
578
728
|
|
|
579
|
-
export { type CaptureFeil, type CaptureInput, type CaptureOnske, type CaptureResult, type ContextInput, type CommonCorrelation as Correlation, type CommonErrorCode as ErrorCode, type FlushResult, type IdentityProvider, InnspelApiError, type InnspelClient, InnspelConfigError, type InnspelOptions, InnspelQueueFullError, type InnspelStorage, KONTEKST_FELTER, KO_TAK, type KnownIssuesKnownIssueCard as KnownIssueCard, type KnownIssueQuery, type SubmissionsKnownIssueRef as KnownIssueRef, type KnownIssuesKnownIssueStatus as KnownIssueStatus, type ProgramsMyPrograms as MyPrograms, type MySubmission, type OpenFeedbackOptions, type PatternFinding, type PatternKind, type CommonPlatform as Platform, type Presenter, type QueuedSubmission, type SubmissionsReasonCode as ReasonCode, type SubmissionsReplyView as ReplyView, type ResolvedSettings, type SubmissionsStatusType as StatusType, type SubmissionsSubmissionStatus as SubmissionStatus, type SubmissionsSubmissionStatusView as SubmissionStatusView, type SubmissionsSubmissionType as SubmissionType, type Telemetry, type TelemetryEvent, type TextMaxLength, VERSJON, init, scanText };
|
|
729
|
+
export { type AttachmentBody, type AttachmentInput, type SubmissionsAttachmentMime as AttachmentMime, type AttachmentOutcome, type SubmissionsAttachmentView as AttachmentView, type CaptureFeil, type CaptureInput, type CaptureOnske, type CaptureResult, type ContextInput, type CommonCorrelation as Correlation, type CommonErrorCode as ErrorCode, type FlushResult, type IdentityProvider, InnspelApiError, type InnspelClient, InnspelConfigError, type InnspelOptions, InnspelQueueFullError, type InnspelStorage, KONTEKST_FELTER, KO_TAK, type KnownIssuesKnownIssueCard as KnownIssueCard, type KnownIssueQuery, type SubmissionsKnownIssueRef as KnownIssueRef, type KnownIssuesKnownIssueStatus as KnownIssueStatus, type MaskPoint, type MaskRect, type ProgramsMyPrograms as MyPrograms, type MySubmission, type OpenFeedbackOptions, type PatternFinding, type PatternKind, type CommonPlatform as Platform, type Presenter, type QueuedSubmission, type SubmissionsReasonCode as ReasonCode, type SubmissionsReplyView as ReplyView, type ResolvedSettings, type SubmissionsStatusType as StatusType, type SubmissionsSubmissionStatus as SubmissionStatus, type SubmissionsSubmissionStatusView as SubmissionStatusView, type SubmissionsSubmissionType as SubmissionType, TILLATTE_MIME, type Telemetry, type TelemetryEvent, type TextMaxLength, VEDLEGG_GRENSER, VERSJON, dekkerAlt, init, klipp, scanText, skaler, strokTilRekter, treffer, trykkTilRekt };
|
package/dist/index.js
CHANGED
|
@@ -778,6 +778,28 @@ var createClient = (config = {}) => {
|
|
|
778
778
|
var client = createClient(createConfig());
|
|
779
779
|
|
|
780
780
|
// src/generated/api/sdk.gen.ts
|
|
781
|
+
var attachmentsPresign = (options) => (options.client ?? client).post({
|
|
782
|
+
security: [{ name: "X-Innspel-Tenant-Key", type: "apiKey" }, {
|
|
783
|
+
key: "FeedbackTokenAuth",
|
|
784
|
+
scheme: "bearer",
|
|
785
|
+
type: "http"
|
|
786
|
+
}],
|
|
787
|
+
url: "/v1/ingest/attachments/presign",
|
|
788
|
+
...options,
|
|
789
|
+
headers: {
|
|
790
|
+
"Content-Type": "application/json",
|
|
791
|
+
...options.headers
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
var attachmentsComplete = (options) => (options.client ?? client).post({
|
|
795
|
+
security: [{ name: "X-Innspel-Tenant-Key", type: "apiKey" }, {
|
|
796
|
+
key: "FeedbackTokenAuth",
|
|
797
|
+
scheme: "bearer",
|
|
798
|
+
type: "http"
|
|
799
|
+
}],
|
|
800
|
+
url: "/v1/ingest/attachments/{id}/complete",
|
|
801
|
+
...options
|
|
802
|
+
});
|
|
781
803
|
var knownIssuesList2 = (options) => (options?.client ?? client).get({
|
|
782
804
|
security: [{ name: "X-Innspel-Tenant-Key", type: "apiKey" }, {
|
|
783
805
|
key: "FeedbackTokenAuth",
|
|
@@ -1127,8 +1149,66 @@ function nyClientRef() {
|
|
|
1127
1149
|
return hex.join("");
|
|
1128
1150
|
}
|
|
1129
1151
|
|
|
1152
|
+
// src/vedlegg.ts
|
|
1153
|
+
var VEDLEGG_GRENSER = {
|
|
1154
|
+
maxBytes: 5242880,
|
|
1155
|
+
maxDimension: 4096
|
|
1156
|
+
};
|
|
1157
|
+
var TILLATTE_MIME = ["image/png", "image/jpeg", "image/webp"];
|
|
1158
|
+
function byteLengde(body) {
|
|
1159
|
+
if (typeof body === "object" && body !== null && "size" in body && typeof body.size === "number") {
|
|
1160
|
+
return body.size;
|
|
1161
|
+
}
|
|
1162
|
+
return body.byteLength;
|
|
1163
|
+
}
|
|
1164
|
+
function validerVedlegg(input) {
|
|
1165
|
+
if (input === null || typeof input !== "object") {
|
|
1166
|
+
throw new InnspelConfigError("attachment", "m\xE5 v\xE6re et objekt med { submissionId?, mime, body, width, height }");
|
|
1167
|
+
}
|
|
1168
|
+
if (!TILLATTE_MIME.includes(input.mime)) {
|
|
1169
|
+
throw new InnspelConfigError(
|
|
1170
|
+
"attachment.mime",
|
|
1171
|
+
`m\xE5 v\xE6re en av ${TILLATTE_MIME.join(", ")}. SVG og GIF er aldri tillatt (K9)`
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
if (input.body === null || input.body === void 0) {
|
|
1175
|
+
throw new InnspelConfigError("attachment.body", "mangler \u2014 send bildet som Blob, ArrayBuffer eller Uint8Array");
|
|
1176
|
+
}
|
|
1177
|
+
const bytes = byteLengde(input.body);
|
|
1178
|
+
if (!Number.isFinite(bytes) || bytes < 1) {
|
|
1179
|
+
throw new InnspelConfigError("attachment.body", "er tom");
|
|
1180
|
+
}
|
|
1181
|
+
if (bytes > VEDLEGG_GRENSER.maxBytes) {
|
|
1182
|
+
throw new InnspelConfigError(
|
|
1183
|
+
"attachment.body",
|
|
1184
|
+
`er ${bytes} byte, over taket p\xE5 ${VEDLEGG_GRENSER.maxBytes} (5 MB). Reduser kvaliteten eller oppl\xF8sningen ved fangst`
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
for (const felt of ["width", "height"]) {
|
|
1188
|
+
const v = input[felt];
|
|
1189
|
+
if (!Number.isInteger(v) || v < 1 || v > VEDLEGG_GRENSER.maxDimension) {
|
|
1190
|
+
throw new InnspelConfigError(
|
|
1191
|
+
`attachment.${felt}`,
|
|
1192
|
+
`m\xE5 v\xE6re et heltall mellom 1 og ${VEDLEGG_GRENSER.maxDimension} (K9)`
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
async function lastOppTilLagring(fetchImpl, presign, body, signal) {
|
|
1198
|
+
const headers = {};
|
|
1199
|
+
for (const h of presign.headers) headers[h.name] = h.value;
|
|
1200
|
+
const res = await fetchImpl(new Request(presign.uploadUrl, { method: "PUT", headers, body, signal }));
|
|
1201
|
+
if (!res.ok) {
|
|
1202
|
+
throw new InnspelApiError(
|
|
1203
|
+
res.status,
|
|
1204
|
+
"unknown",
|
|
1205
|
+
`Lagringen avviste opplastingen med status ${res.status}. Bildet ble ikke lagt ved.`
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1130
1210
|
// src/versjon.ts
|
|
1131
|
-
var VERSJON = "0.
|
|
1211
|
+
var VERSJON = "0.2.0";
|
|
1132
1212
|
|
|
1133
1213
|
// src/klient.ts
|
|
1134
1214
|
var STANDARD_TIMEOUT_MS = 1e4;
|
|
@@ -1270,6 +1350,31 @@ function init(options) {
|
|
|
1270
1350
|
})
|
|
1271
1351
|
);
|
|
1272
1352
|
}
|
|
1353
|
+
async function lastOppVedlegg(submissionId, vedlegg) {
|
|
1354
|
+
const presign = await medToken(
|
|
1355
|
+
(h) => attachmentsPresign({
|
|
1356
|
+
client: klient,
|
|
1357
|
+
headers: h,
|
|
1358
|
+
signal: signal(),
|
|
1359
|
+
body: {
|
|
1360
|
+
submissionId,
|
|
1361
|
+
mime: vedlegg.mime,
|
|
1362
|
+
bytes: byteLengde(vedlegg.body),
|
|
1363
|
+
width: vedlegg.width,
|
|
1364
|
+
height: vedlegg.height
|
|
1365
|
+
}
|
|
1366
|
+
})
|
|
1367
|
+
);
|
|
1368
|
+
await lastOppTilLagring(options.fetch ?? fetch, presign, vedlegg.body, signal());
|
|
1369
|
+
return medToken(
|
|
1370
|
+
(h) => attachmentsComplete({
|
|
1371
|
+
client: klient,
|
|
1372
|
+
headers: h,
|
|
1373
|
+
signal: signal(),
|
|
1374
|
+
path: { id: presign.attachmentId }
|
|
1375
|
+
})
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1273
1378
|
const flushK\u00F8 = () => flushKo(
|
|
1274
1379
|
k\u00F8,
|
|
1275
1380
|
(rad) => sendN\u00E5({ type: rad.type, text: rad.text }, rad.clientRef, kontekst),
|
|
@@ -1295,17 +1400,12 @@ function init(options) {
|
|
|
1295
1400
|
);
|
|
1296
1401
|
}
|
|
1297
1402
|
if (input.context) validerKontekst(input.context, "capture.context", false);
|
|
1403
|
+
if (input.attachment) validerVedlegg(input.attachment);
|
|
1298
1404
|
const clientRef = input.clientRef ?? nyClientRef();
|
|
1299
1405
|
const kontekstForKall = sl\u00E5SammenKontekst(options.context, input.context, VERSJON);
|
|
1406
|
+
let submission;
|
|
1300
1407
|
try {
|
|
1301
|
-
|
|
1302
|
-
telemetri.emit({
|
|
1303
|
-
name: "submission_sent",
|
|
1304
|
-
type: input.type,
|
|
1305
|
-
hasAttachment: false,
|
|
1306
|
-
offlineQueued: false
|
|
1307
|
-
});
|
|
1308
|
-
return { status: "sent", submission };
|
|
1408
|
+
submission = await sendN\u00E5(input, clientRef, kontekstForKall);
|
|
1309
1409
|
} catch (e) {
|
|
1310
1410
|
if (e instanceof InnspelApiError) {
|
|
1311
1411
|
telemetri.emit({ name: "submission_failed", reason: e.code });
|
|
@@ -1323,8 +1423,23 @@ function init(options) {
|
|
|
1323
1423
|
hasAttachment: false,
|
|
1324
1424
|
offlineQueued: true
|
|
1325
1425
|
});
|
|
1326
|
-
return { status: "queued", clientRef };
|
|
1426
|
+
return input.attachment ? { status: "queued", clientRef, attachment: { status: "dropped" } } : { status: "queued", clientRef };
|
|
1427
|
+
}
|
|
1428
|
+
let attachment;
|
|
1429
|
+
if (input.attachment) {
|
|
1430
|
+
try {
|
|
1431
|
+
attachment = { status: "uploaded", attachment: await lastOppVedlegg(submission.id, input.attachment) };
|
|
1432
|
+
} catch (e) {
|
|
1433
|
+
attachment = { status: "failed", error: e instanceof Error ? e : new Error(String(e)) };
|
|
1434
|
+
}
|
|
1327
1435
|
}
|
|
1436
|
+
telemetri.emit({
|
|
1437
|
+
name: "submission_sent",
|
|
1438
|
+
type: input.type,
|
|
1439
|
+
hasAttachment: attachment?.status === "uploaded",
|
|
1440
|
+
offlineQueued: false
|
|
1441
|
+
});
|
|
1442
|
+
return attachment ? { status: "sent", submission, attachment } : { status: "sent", submission };
|
|
1328
1443
|
},
|
|
1329
1444
|
knownIssues: {
|
|
1330
1445
|
async for(query = {}) {
|
|
@@ -1404,6 +1519,15 @@ function init(options) {
|
|
|
1404
1519
|
);
|
|
1405
1520
|
}
|
|
1406
1521
|
},
|
|
1522
|
+
attachments: {
|
|
1523
|
+
async upload(submissionId, attachment) {
|
|
1524
|
+
if (typeof submissionId !== "string" || submissionId.length === 0) {
|
|
1525
|
+
throw new InnspelConfigError("submissionId", "mangler \u2014 bruk id fra innsendingen capture() ga");
|
|
1526
|
+
}
|
|
1527
|
+
validerVedlegg(attachment);
|
|
1528
|
+
return lastOppVedlegg(submissionId, attachment);
|
|
1529
|
+
}
|
|
1530
|
+
},
|
|
1407
1531
|
queue: {
|
|
1408
1532
|
pending: () => k\u00F8.les(),
|
|
1409
1533
|
flush: flushK\u00F8
|
|
@@ -1427,6 +1551,57 @@ function init(options) {
|
|
|
1427
1551
|
return innspel;
|
|
1428
1552
|
}
|
|
1429
1553
|
|
|
1430
|
-
|
|
1554
|
+
// src/maskering.ts
|
|
1555
|
+
function strokTilRekter(punkter, penselBredde, bilde) {
|
|
1556
|
+
if (penselBredde <= 0 || punkter.length === 0) return [];
|
|
1557
|
+
const halv = penselBredde / 2;
|
|
1558
|
+
const ut = [];
|
|
1559
|
+
const legg = (p) => {
|
|
1560
|
+
const r = klipp({ x: p.x - halv, y: p.y - halv, width: penselBredde, height: penselBredde }, bilde);
|
|
1561
|
+
if (r) ut.push(r);
|
|
1562
|
+
};
|
|
1563
|
+
legg(punkter[0]);
|
|
1564
|
+
for (let i = 1; i < punkter.length; i++) {
|
|
1565
|
+
const a = punkter[i - 1];
|
|
1566
|
+
const b = punkter[i];
|
|
1567
|
+
const avstand = Math.hypot(b.x - a.x, b.y - a.y);
|
|
1568
|
+
const steg = Math.max(1, Math.ceil(avstand / halv));
|
|
1569
|
+
for (let s = 1; s <= steg; s++) {
|
|
1570
|
+
legg({ x: a.x + (b.x - a.x) * s / steg, y: a.y + (b.y - a.y) * s / steg });
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
return ut;
|
|
1574
|
+
}
|
|
1575
|
+
function trykkTilRekt(punkt, boks, bilde) {
|
|
1576
|
+
return klipp({ x: punkt.x - boks.width / 2, y: punkt.y - boks.height / 2, width: boks.width, height: boks.height }, bilde);
|
|
1577
|
+
}
|
|
1578
|
+
function klipp(r, bilde) {
|
|
1579
|
+
const x1 = Math.max(0, r.x);
|
|
1580
|
+
const y1 = Math.max(0, r.y);
|
|
1581
|
+
const x2 = Math.min(bilde.width, r.x + r.width);
|
|
1582
|
+
const y2 = Math.min(bilde.height, r.y + r.height);
|
|
1583
|
+
if (x2 <= x1 || y2 <= y1) return null;
|
|
1584
|
+
return { x: x1, y: y1, width: x2 - x1, height: y2 - y1 };
|
|
1585
|
+
}
|
|
1586
|
+
function skaler(r, fra, til) {
|
|
1587
|
+
const sx = til.width / fra.width;
|
|
1588
|
+
const sy = til.height / fra.height;
|
|
1589
|
+
const x = Math.floor(r.x * sx);
|
|
1590
|
+
const y = Math.floor(r.y * sy);
|
|
1591
|
+
return {
|
|
1592
|
+
x,
|
|
1593
|
+
y,
|
|
1594
|
+
width: Math.ceil((r.x + r.width) * sx) - x,
|
|
1595
|
+
height: Math.ceil((r.y + r.height) * sy) - y
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
function treffer(punkt, r) {
|
|
1599
|
+
return punkt.x >= r.x && punkt.x < r.x + r.width && punkt.y >= r.y && punkt.y < r.y + r.height;
|
|
1600
|
+
}
|
|
1601
|
+
function dekkerAlt(rekter, bilde) {
|
|
1602
|
+
return rekter.some((r) => r.x <= 0 && r.y <= 0 && r.x + r.width >= bilde.width && r.y + r.height >= bilde.height);
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
export { InnspelApiError, InnspelConfigError, InnspelQueueFullError, KONTEKST_FELTER, KO_TAK, TILLATTE_MIME, VEDLEGG_GRENSER, VERSJON, dekkerAlt, init, klipp, scanText, skaler, strokTilRekter, treffer, trykkTilRekt };
|
|
1431
1606
|
//# sourceMappingURL=index.js.map
|
|
1432
1607
|
//# sourceMappingURL=index.js.map
|