@preventive/triage 1.0.0-alpha.3 → 1.0.0-alpha.30
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/LICENSE-APACHE +202 -0
- package/api/reap.ts +17 -0
- package/cli.js +6 -0
- package/client/finding-link.js +305 -0
- package/client/linked-findings.d.ts +1 -0
- package/client/linked-findings.js +111 -0
- package/common/bundle-commit.d.ts +1 -0
- package/common/bundle-commit.js +4 -0
- package/common/bundle-metadata.d.ts +10 -0
- package/common/bundle-metadata.js +182 -0
- package/common/bundle-reasons.d.ts +2 -0
- package/common/bundle-reasons.js +21 -0
- package/common/bundle-source-links.d.ts +5 -0
- package/common/bundle-source-links.js +82 -0
- package/common/bundle-sources.d.ts +3 -0
- package/common/bundle-sources.js +284 -0
- package/common/bundle-stats.js +41 -0
- package/common/bundle-tabs.js +1 -0
- package/common/code-language.js +51 -0
- package/common/default-scan-models.ts +30 -0
- package/common/finding-id.js +47 -0
- package/common/github-issue-labels.d.ts +1 -0
- package/common/github-issue-labels.js +11 -0
- package/common/github-pr.ts +56 -0
- package/common/managed/comments.ts +34 -0
- package/common/managed/permissions.ts +35 -0
- package/common/managed/report-content.ts +42 -0
- package/common/managed/report-filter.ts +108 -0
- package/common/managed/roles.ts +28 -0
- package/common/managed/routes.d.ts +2 -0
- package/common/managed/routes.js +124 -0
- package/common/managed/scan-models.ts +6 -0
- package/common/managed/triage.ts +83 -0
- package/common/save-error-reason.ts +20 -7
- package/common/scan-server.ts +13 -0
- package/common/server-info.ts +35 -0
- package/common/utf8.d.ts +3 -0
- package/common/utf8.js +45 -0
- package/out/brotli-fallback.js +3 -3
- package/out/client-managed-import.js +81 -0
- package/out/client-managed.js +167 -0
- package/out/client-sync.js +16 -13
- package/out/github-issue-authorized.html +3 -0
- package/out/github-issue-authorized.js +1 -0
- package/out/graph.js +30 -4
- package/out/index.html +55 -8
- package/out/prism.js +2 -2
- package/out/stasis.svg +20 -0
- package/out/terminal.js +273 -39
- package/out/view.css +1 -1
- package/out/view.js +259 -63
- package/package.json +205 -57
- package/server-common/database-config.ts +16 -0
- package/server-common/initialize.ts +18 -0
- package/server-common/npm-advisories.ts +101 -0
- package/{server → server-common}/origin.ts +5 -5
- package/server-common/reap.ts +48 -0
- package/server-common/scan-config.ts +19 -0
- package/server-common/standalone.ts +29 -0
- package/server-common/storage-crypto.ts +182 -0
- package/server-common/storage-log.ts +38 -0
- package/server-common/vercel-blob.ts +102 -0
- package/server-e2e/app.ts +486 -0
- package/{server → server-e2e}/auth.ts +5 -1
- package/{server → server-e2e}/bus-receiver.ts +2 -1
- package/{server → server-e2e}/cli.js +9 -4
- package/{server → server-e2e}/config.ts +56 -35
- package/{server → server-e2e}/db-neon.ts +2 -2
- package/{server → server-e2e}/db-revision-sql.ts +1 -1
- package/{server → server-e2e}/db-stmt.ts +2 -2
- package/{server → server-e2e}/db.ts +7 -7
- package/{server → server-e2e}/http.ts +110 -12
- package/{server → server-e2e}/hub.ts +40 -9
- package/server-e2e/index.ts +17 -0
- package/server-e2e/lifecycle.ts +95 -0
- package/{server → server-e2e}/neon-driver.ts +2 -2
- package/{server → server-e2e}/npm-proxy.ts +11 -144
- package/{server → server-e2e}/objstore/blob-fs.ts +2 -2
- package/{server → server-e2e}/objstore/blob-vercel.ts +13 -117
- package/{server → server-e2e}/objstore/blob.ts +9 -2
- package/server-e2e/objstore/fetch-mint-guard.ts +74 -0
- package/{server → server-e2e}/objstore/handlers.ts +9 -8
- package/{server → server-e2e}/objstore/init.ts +35 -18
- package/{server → server-e2e}/objstore/reaper.ts +31 -11
- package/server-e2e/objstore/rest-deny.ts +28 -0
- package/server-e2e/objstore/rest-mint.ts +224 -0
- package/{server → server-e2e}/objstore/rest.ts +58 -33
- package/{server → server-e2e}/objstore/sign.ts +105 -0
- package/{server → server-e2e}/objstore/store-neon.ts +6 -10
- package/{server → server-e2e}/objstore/store.ts +7 -7
- package/{server → server-e2e}/objstore/tokens.ts +1 -1
- package/{server → server-e2e}/pubsub.ts +12 -9
- package/{server → server-e2e}/sign.ts +4 -4
- package/{server → server-e2e}/sse-server.ts +102 -68
- package/{server → server-e2e}/sse-session.ts +28 -8
- package/{server → server-e2e}/static.ts +31 -21
- package/server-e2e/sync-handlers.ts +408 -0
- package/{server → server-e2e}/util.ts +9 -0
- package/{server → server-e2e}/ws-server.ts +20 -9
- package/server-managed/activity.ts +231 -0
- package/server-managed/avatar-store.ts +12 -0
- package/server-managed/blob-store.ts +17 -0
- package/server-managed/brotli.ts +10 -0
- package/server-managed/bundle-advisories.ts +53 -0
- package/server-managed/bundle-advisory-inventory.ts +75 -0
- package/server-managed/bundle-build-diagnostics.d.ts +10 -0
- package/server-managed/bundle-build-diagnostics.js +23 -0
- package/server-managed/bundle-build-leases.ts +41 -0
- package/server-managed/bundle-build-worker.js +54 -0
- package/server-managed/bundle-build.ts +168 -0
- package/server-managed/bundle-cache.ts +166 -0
- package/server-managed/bundle-catalog.ts +29 -0
- package/server-managed/bundle-store.ts +24 -0
- package/server-managed/bundle-summary-cache.ts +116 -0
- package/server-managed/bundle.ts +104 -0
- package/server-managed/cache-storage.ts +19 -0
- package/server-managed/cli.js +21 -0
- package/server-managed/combined.ts +46 -0
- package/server-managed/comments.ts +151 -0
- package/server-managed/config.ts +149 -0
- package/server-managed/content-access.ts +15 -0
- package/server-managed/crypto.ts +25 -0
- package/server-managed/db-methods.ts +1517 -0
- package/server-managed/db-neon.ts +277 -0
- package/server-managed/db-schema.ts +206 -0
- package/server-managed/db-table-names.ts +22 -0
- package/server-managed/db.ts +120 -0
- package/server-managed/github-app.ts +385 -0
- package/server-managed/github-errors.ts +49 -0
- package/server-managed/github-issue-oauth.ts +48 -0
- package/server-managed/github-issues.ts +152 -0
- package/server-managed/github-metadata.ts +98 -0
- package/server-managed/github-oauth.ts +199 -0
- package/server-managed/github-pulls.ts +227 -0
- package/server-managed/http-response.ts +18 -0
- package/server-managed/http.ts +2350 -0
- package/server-managed/import-triage.ts +51 -0
- package/server-managed/index.ts +175 -0
- package/server-managed/managed-issues.ts +47 -0
- package/server-managed/management-catalog.ts +45 -0
- package/server-managed/management.ts +86 -0
- package/server-managed/object-storage-disk.ts +144 -0
- package/server-managed/object-storage-vercel.ts +60 -0
- package/server-managed/object-storage.ts +71 -0
- package/server-managed/package-entry-points.ts +51 -0
- package/server-managed/public-workspace.ts +146 -0
- package/server-managed/repo-path.ts +21 -0
- package/server-managed/report-migration.ts +35 -0
- package/server-managed/report-query.ts +10 -0
- package/server-managed/report-response.ts +16 -0
- package/server-managed/report-sources.ts +166 -0
- package/server-managed/repository-browser.ts +139 -0
- package/server-managed/repository-discovery.ts +82 -0
- package/server-managed/repository-policy.ts +27 -0
- package/server-managed/revisions.ts +35 -0
- package/server-managed/session.ts +78 -0
- package/server-managed/slugs.ts +38 -0
- package/server-managed/solidity-entry-points.ts +69 -0
- package/server-managed/sql-postgres.ts +31 -0
- package/server-managed/sql.ts +72 -0
- package/server-managed/static.ts +28 -0
- package/server-managed/storage-command.ts +16 -0
- package/server-managed/storage-db.ts +175 -0
- package/server-managed/storage-encryption.ts +211 -0
- package/server-managed/storage-maintenance.ts +223 -0
- package/server-managed/storage-payload.ts +72 -0
- package/server-managed/storage-stores.ts +81 -0
- package/server-managed/storage.ts +58 -0
- package/server-managed/team-catalog.ts +7 -0
- package/server-managed/team-feed.ts +173 -0
- package/server-managed/team-reports.ts +219 -0
- package/server-managed/triage-response.ts +16 -0
- package/server-managed/uploads.ts +65 -0
- package/server-managed/workspace-shares.ts +162 -0
- package/server.ts +50 -0
- package/server/index.ts +0 -477
- package/server/lifecycle.ts +0 -204
- package/server/sync-handlers.ts +0 -320
- /package/{LICENSE → LICENSE-MIT} +0 -0
- /package/{server → server-e2e}/config.example.json +0 -0
- /package/{server → server-e2e}/objstore/fs.ts +0 -0
- /package/{server → server-e2e}/peer.ts +0 -0
- /package/{server → server-e2e}/validation.ts +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Per-finding triage annotations on a managed server — shared by the server
|
|
2
|
+
// (which stores one row per finding id, validates the wire shape, and decides
|
|
3
|
+
// per report which ids a viewer may read or write) and the managed client
|
|
4
|
+
// (which mirrors entries into its local triage map). Keyed by finding id
|
|
5
|
+
// alone, like that map: reports mostly repeat one another (a re-scan of the
|
|
6
|
+
// same code carries the same finding ids), and a finding's triage is shared by
|
|
7
|
+
// every report that carries it. A wire entry carries the four server-persisted
|
|
8
|
+
// fields below, or is null for a cleared entry (the server's tombstone); the
|
|
9
|
+
// client's `ignoredReports` deliberately does NOT ride this wire — the
|
|
10
|
+
// per-report ignore is a client-local concept keyed by report name.
|
|
11
|
+
export type TriageBucket = 'inprogress' | 'fixed' | 'invalid' | 'deleted'
|
|
12
|
+
|
|
13
|
+
export const TRIAGE_BUCKETS: readonly TriageBucket[] = ['inprogress', 'fixed', 'invalid', 'deleted']
|
|
14
|
+
|
|
15
|
+
// One finding's server-side triage entry (also the write shape — writes
|
|
16
|
+
// replace the whole entry). Absent fields are unset; `flagged` is tri-state:
|
|
17
|
+
// absent = never set, `true` = flagged, `false` = an explicit un-flag
|
|
18
|
+
// tombstone that must round-trip (see client/state.ts TriageEntry).
|
|
19
|
+
export type TriageEntryPatch = {
|
|
20
|
+
color?: string
|
|
21
|
+
triage?: TriageBucket
|
|
22
|
+
// Legacy triage history/migration only. New comments use separate records;
|
|
23
|
+
// the managed triage write endpoint rejects this field.
|
|
24
|
+
comment?: string
|
|
25
|
+
fix?: string
|
|
26
|
+
flagged?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Length caps enforced at the write endpoint so one entry can't balloon a row.
|
|
30
|
+
export const MAX_TRIAGE_TEXT = 10_000
|
|
31
|
+
export const MAX_TRIAGE_COLOR = 50
|
|
32
|
+
|
|
33
|
+
// Caps on one write request as a whole — shared with the client, which batches
|
|
34
|
+
// within them: entries per request, the finding-id length (real ids are 36-char
|
|
35
|
+
// uuids), and the JSON body (the server's small default is too small for entry
|
|
36
|
+
// batches, which carry free text).
|
|
37
|
+
export const MAX_TRIAGE_ENTRIES = 200
|
|
38
|
+
export const MAX_FINDING_ID = 100
|
|
39
|
+
export const MAX_TRIAGE_BODY_BYTES = 262_144
|
|
40
|
+
|
|
41
|
+
// The most trail events one history read returns (newest first). Retention is
|
|
42
|
+
// the server's: everything is kept unless its TRIAGE_HISTORY_LIMIT says
|
|
43
|
+
// otherwise.
|
|
44
|
+
export const MAX_TRIAGE_HISTORY = 200
|
|
45
|
+
|
|
46
|
+
export function isTriageBucket(x: unknown): x is TriageBucket {
|
|
47
|
+
return typeof x === 'string' && (TRIAGE_BUCKETS as readonly string[]).includes(x)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Coerce one wire entry (a request-body value) into a clean patch. `null`
|
|
51
|
+
// means "clear the entry"; a malformed value (wrong type, unknown bucket,
|
|
52
|
+
// over-cap string, non-boolean flagged) is 'invalid' so the endpoint 400s
|
|
53
|
+
// rather than store garbage. Null/empty-string fields count as absent (an
|
|
54
|
+
// absent field is cleared anyway under whole-entry replace); unknown keys are
|
|
55
|
+
// ignored.
|
|
56
|
+
export function parseTriageEntryPatch(x: unknown): TriageEntryPatch | null | 'invalid' {
|
|
57
|
+
if (x === null) return null
|
|
58
|
+
if (typeof x !== 'object' || Array.isArray(x)) return 'invalid'
|
|
59
|
+
const o = x as Record<string, unknown>
|
|
60
|
+
const out: TriageEntryPatch = {}
|
|
61
|
+
const color = o['color']
|
|
62
|
+
if (color != null) {
|
|
63
|
+
if (typeof color !== 'string' || color.length > MAX_TRIAGE_COLOR) return 'invalid'
|
|
64
|
+
if (color !== '') out.color = color
|
|
65
|
+
}
|
|
66
|
+
const triage = o['triage']
|
|
67
|
+
if (triage != null) {
|
|
68
|
+
if (!isTriageBucket(triage)) return 'invalid'
|
|
69
|
+
out.triage = triage
|
|
70
|
+
}
|
|
71
|
+
for (const key of ['comment', 'fix'] as const) {
|
|
72
|
+
const v = o[key]
|
|
73
|
+
if (v == null) continue
|
|
74
|
+
if (typeof v !== 'string' || v.length > MAX_TRIAGE_TEXT) return 'invalid'
|
|
75
|
+
if (v !== '') out[key] = v
|
|
76
|
+
}
|
|
77
|
+
const flagged = o['flagged']
|
|
78
|
+
if (flagged != null) {
|
|
79
|
+
if (typeof flagged !== 'boolean') return 'invalid'
|
|
80
|
+
out.flagged = flagged
|
|
81
|
+
}
|
|
82
|
+
return out
|
|
83
|
+
}
|
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
// `session.error` until `dismissError()`.
|
|
16
16
|
// `busy` — per-socket inflight cap dropped the save
|
|
17
17
|
// (transport backpressure). Recoverable; the
|
|
18
|
-
// client clears `pending
|
|
19
|
-
//
|
|
18
|
+
// client clears `pending`, re-arms `pendingSave`,
|
|
19
|
+
// and schedules a short timed retry — without the
|
|
20
|
+
// timer an idle client (no further edit /
|
|
21
|
+
// broadcast / reconnect) would strand its last
|
|
22
|
+
// edit unsynced indefinitely.
|
|
20
23
|
// `stale-base` — concurrent commit advanced the head past the
|
|
21
24
|
// client's claimed base. The server emits the
|
|
22
25
|
// typed frame AFTER a `workspace-state` catch-up
|
|
@@ -25,11 +28,21 @@
|
|
|
25
28
|
// and this frame's handler then early-returns
|
|
26
29
|
// on the missing pending. So `stale-base` is
|
|
27
30
|
// NOT in the recoverable set: in the happy path
|
|
28
|
-
// the recoverable branch is unreachable
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
31
|
+
// the recoverable branch is unreachable. When
|
|
32
|
+
// `pending` DOES survive to this frame — the
|
|
33
|
+
// catch-up was empty (chain gone: wiped /
|
|
34
|
+
// migrated DB) or didn't apply (chain rebuilt
|
|
35
|
+
// past the client's base) — the client answers
|
|
36
|
+
// with ONE full-state-push reset per recovery
|
|
37
|
+
// cycle (re-anchor at base=null; safe because
|
|
38
|
+
// the server's null-safe head CAS rejects a
|
|
39
|
+
// base=null root against any non-empty chain;
|
|
40
|
+
// see `staleResetAttempted` in triage-sync.ts).
|
|
41
|
+
// A repeat within the cycle marks the session
|
|
42
|
+
// errored (visible via UI + recoverable via
|
|
43
|
+
// `dismissError()`) rather than looping
|
|
44
|
+
// full-state saves against a relay that keeps
|
|
45
|
+
// rejecting them.
|
|
33
46
|
|
|
34
47
|
export type SaveErrorReason = 'too-large' | 'busy' | 'stale-base'
|
|
35
48
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const DEFAULT_SCAN_SERVER = 'http://127.0.0.1:3123/'
|
|
2
|
+
|
|
3
|
+
// Discovery is optional: malformed scan configuration must never prevent
|
|
4
|
+
// opening local data or recognizing the server's sync protocol.
|
|
5
|
+
export function normalizeScanServer(value: unknown): string | null {
|
|
6
|
+
if (typeof value !== 'string' || !value.trim()) return null
|
|
7
|
+
try {
|
|
8
|
+
const url = new URL(value)
|
|
9
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash) return null
|
|
10
|
+
url.pathname = url.pathname.replace(/\/+$/u, '') + '/'
|
|
11
|
+
return url.href
|
|
12
|
+
} catch { return null }
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// The sync-protocol mode a server advertises — shared by the server (which
|
|
2
|
+
// emits it) and the client (which parses + caches it) so a change to the
|
|
3
|
+
// shape surfaces as a compile error on BOTH sides, the same single-source
|
|
4
|
+
// discipline as ./save-error-reason.ts.
|
|
5
|
+
//
|
|
6
|
+
// Delivered to the client as the first `server-info` frame on a sync
|
|
7
|
+
// connection (the WS plane and the SSE+POST fallback share one send path).
|
|
8
|
+
// A client uses it to detect whether a deployment speaks the end-to-end
|
|
9
|
+
// (`e2e`, zero-knowledge) protocol, the trusted `managed` protocol, or both.
|
|
10
|
+
// Combined discovery modes put the default protocol first.
|
|
11
|
+
|
|
12
|
+
export type ServerMode = 'e2e' | 'managed'
|
|
13
|
+
|
|
14
|
+
// Managed-mode entry points the server advertises; null for an e2e server.
|
|
15
|
+
export interface ManagedServerInfo {
|
|
16
|
+
loginPath: string
|
|
17
|
+
cookieName: string
|
|
18
|
+
uploadChunkBytes?: number
|
|
19
|
+
uploadMaxBytes?: { reports: number; bundles: number }
|
|
20
|
+
allowShare?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The `server-info` frame payload (the `type` discriminant is added at the
|
|
24
|
+
// send site). `managed` is null on e2e-only deployments.
|
|
25
|
+
export interface ServerInfo {
|
|
26
|
+
mode: ServerMode | 'managed+e2e' | 'e2e+managed'
|
|
27
|
+
managed: ManagedServerInfo | null
|
|
28
|
+
deepviewScanServer?: string
|
|
29
|
+
githubNewIssueLabels?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The mode-probe route. A client GETs this to learn a server's protocol up
|
|
33
|
+
// front — a managed server has no WS plane to carry the connect frame — and
|
|
34
|
+
// the response body is the same `ServerInfo` shape emitted as that frame.
|
|
35
|
+
export const CONFIG_PATH = '/api/config'
|
package/common/utf8.d.ts
CHANGED
|
@@ -11,3 +11,6 @@
|
|
|
11
11
|
// narrower declaration is safe.
|
|
12
12
|
export function encodeUtf8(str: string): Uint8Array<ArrayBuffer>
|
|
13
13
|
export function decodeUtf8(bytes: Uint8Array | ArrayBuffer | ArrayBufferView): string
|
|
14
|
+
// Bytes `str` takes in UTF-8, measured without encoding when no character
|
|
15
|
+
// is past \xFF. A lone surrogate counts as the U+FFFD it encodes to.
|
|
16
|
+
export function utf8ByteLength(str: string): number
|
package/common/utf8.js
CHANGED
|
@@ -43,6 +43,51 @@ export function encodeUtf8(str) {
|
|
|
43
43
|
return encoder.encode(str)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// ASCII characters in a row after which the count hands back to the
|
|
47
|
+
// native scan. A jump costs a call and a step does not, so it pays only
|
|
48
|
+
// across a real gap: at 1, alternating text (`aéaé…`) takes a jump per
|
|
49
|
+
// character, and past 2, text with an accent every few words steps
|
|
50
|
+
// through ASCII the scan crosses faster. Measured on 50 MB of each, 2 was
|
|
51
|
+
// never more than 1.5x the best of the thresholds tried, 1 to 32.
|
|
52
|
+
const ASCII_RUN = 2
|
|
53
|
+
|
|
54
|
+
// The number of bytes `str` takes in UTF-8, without encoding it where
|
|
55
|
+
// that can be helped. A string with no character past \xFF is Latin-1:
|
|
56
|
+
// ASCII takes a byte and \x80-\xFF two, so its size is its length plus
|
|
57
|
+
// its high characters. An engine stores such a string a byte per
|
|
58
|
+
// character, where no character past \xFF can be, so the first test is
|
|
59
|
+
// answered without reading it. Only a string with a wider character is
|
|
60
|
+
// encoded to be measured.
|
|
61
|
+
//
|
|
62
|
+
// The high characters are counted without collecting them: a match array
|
|
63
|
+
// takes an entry apiece, which for Latin-1-heavy text outweighs the
|
|
64
|
+
// encoding this avoids. A native scan jumps to the next one — for ASCII
|
|
65
|
+
// it finds none — and a loop counts on from there while they keep coming,
|
|
66
|
+
// handing back to the scan after a run of ASCII. Sparse text is read
|
|
67
|
+
// natively, dense text by the loop, and neither allocates.
|
|
68
|
+
//
|
|
69
|
+
// This sizes text for display, so unlike `encodeUtf8` it does not refuse
|
|
70
|
+
// a lone surrogate: it counts the U+FFFD that TextEncoder writes for one.
|
|
71
|
+
export function utf8ByteLength(str) {
|
|
72
|
+
if (typeof str !== 'string') {
|
|
73
|
+
throw new TypeError(`utf8ByteLength expects a string, got ${typeof str}`)
|
|
74
|
+
}
|
|
75
|
+
// Code units, not code points: a `u` regexp reads a two-byte string by
|
|
76
|
+
// code point, several times slower, and a surrogate is past \xFF either way.
|
|
77
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
78
|
+
if (/[\u0100-\uFFFF]/.test(str)) return encoder.encode(str).byteLength
|
|
79
|
+
const high = /[\u0080-\u00FF]/gu
|
|
80
|
+
let bytes = str.length
|
|
81
|
+
while (high.test(str)) {
|
|
82
|
+
let i = high.lastIndex - 1
|
|
83
|
+
for (let ascii = 0; i < str.length && ascii < ASCII_RUN; i++) {
|
|
84
|
+
if (str.codePointAt(i) > 0x7F) { bytes++; ascii = 0 } else ascii++
|
|
85
|
+
}
|
|
86
|
+
high.lastIndex = i
|
|
87
|
+
}
|
|
88
|
+
return bytes
|
|
89
|
+
}
|
|
90
|
+
|
|
46
91
|
export function decodeUtf8(bytes) {
|
|
47
92
|
// Reject non-BufferSource so a missed destructure / optional field /
|
|
48
93
|
// misnamed property surfaces here instead of silently defaulting to
|