@nxgt/material-core 0.7.1 → 0.9.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/CHANGELOG.md +40 -0
- package/README.md +181 -6
- package/dist/chunks/index-f24x24a8.js +37 -0
- package/dist/chunks/index-f24x24a8.js.map +10 -0
- package/dist/lib/async-search.d.ts +46 -0
- package/dist/lib/async-search.d.ts.map +1 -0
- package/dist/lib/async-search.js +116 -0
- package/dist/lib/async-search.js.map +11 -0
- package/dist/lib/csp-nonce.d.ts +56 -0
- package/dist/lib/csp-nonce.d.ts.map +1 -0
- package/dist/lib/csp-nonce.js +88 -0
- package/dist/lib/csp-nonce.js.map +10 -0
- package/dist/lib/debounce.d.ts +6 -0
- package/dist/lib/debounce.d.ts.map +1 -0
- package/dist/lib/debounce.js +9 -0
- package/dist/lib/debounce.js.map +9 -0
- package/dist/lib/lazy-object.d.ts +31 -0
- package/dist/lib/lazy-object.d.ts.map +1 -0
- package/dist/lib/lazy-object.js +61 -0
- package/dist/lib/lazy-object.js.map +10 -0
- package/dist/lib/search-announcement.d.ts +22 -0
- package/dist/lib/search-announcement.d.ts.map +1 -0
- package/dist/lib/text-match.d.ts +16 -0
- package/dist/lib/text-match.d.ts.map +1 -0
- package/dist/lib/text-match.js +25 -0
- package/dist/lib/text-match.js.map +10 -0
- package/dist/vite/csp-assets-inline-limit.d.ts +26 -0
- package/dist/vite/csp-assets-inline-limit.d.ts.map +1 -0
- package/dist/vite/index.d.ts +4 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +8 -1
- package/dist/vite/index.js.map +5 -4
- package/docs/roadmap.md +14 -1
- package/package.json +37 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# @nxgt/material-core
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#471](https://github.com/softistx/nxgt-material/pull/471) [`3a2889d`](https://github.com/softistx/nxgt-material/commit/3a2889d71f1bfc9039ef43db916f3be3bf287ccd) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New subpath `@nxgt/material-core/csp-nonce`, for a page under a strict Content Security Policy: `getCspNonce()` returns the page's nonce, from the first of the value given to `setCspNonce(nonce)`, `<meta property="csp-nonce" nonce="…">` (what Vite's `html.cspNonce` writes) or `<meta name="csp-nonce" content="…">`, the first `<script nonce>` / `<style nonce>`, then `globalThis.__webpack_nonce__`. `createNoncedStyle(doc?)` creates a `<style>` that carries it. Browser only: without a `document`, `setCspNonce` does nothing and `getCspNonce` returns `undefined`, so a request's nonce never reaches another request through a global. The value lives on `globalThis` under `Symbol.for('nxgt.cspNonce')`, shared by duplicate copies of the package. `getNonce` / `setNonce` are the same functions under `get-nonce`'s names, for a bundler alias.
|
|
8
|
+
|
|
9
|
+
`@nxgt/material-core/vite` also exports `cspAssetsInlineLimit`, a `build.assetsInlineLimit` that never inlines a font (`.woff`, `.woff2`, `.ttf`, `.otf`, `.eot`) as a `data:` URI, which `font-src 'self'` blocks, and leaves every other asset to Vite's default: `build: { assetsInlineLimit: cspAssetsInlineLimit }`.
|
|
10
|
+
|
|
11
|
+
`claimCspNonce(nonce, depth = 0)` is for a provider component: it registers its nonce and returns the function that releases it, so nested providers stay right. The stored nonce is that of the deepest claim still held (the latest among equals): an inner provider unmounting leaves its parent's nonce in place, and releasing the last claim puts back the nonce the app had set with `setCspNonce` before the first one. Children mount before their parents, so `depth` (how many providers enclose it) says which one is the innermost, not the order of the calls.
|
|
12
|
+
|
|
13
|
+
`@nxgt/material` and `@nxgt/material-vue` re-export `getCspNonce` and `setCspNonce` from `/lib` and `cspAssetsInlineLimit` from `/vite`, keep the nonce in this store through `NonceProvider` (which calls `claimCspNonce`), and `@nxgt/material` aliases `get-nonce` onto it.
|
|
14
|
+
|
|
15
|
+
- [#480](https://github.com/softistx/nxgt-material/pull/480) [`634a58e`](https://github.com/softistx/nxgt-material/commit/634a58e3353ebe340e87b0f21858396aa4354654) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New subpath `@nxgt/material-core/lazy-object`: `buildOnce(build)` returns a function that builds its value on the first call and returns that same value after; `lazyObject(build)` returns a stand-in that builds the object on the first operation of any kind (a property read, a method call, `in`, `Object.keys`, `instanceof`) and then answers as it does. The stand-in is not the built object (`===` tells them apart, and a `.meta()` or `registry.add()` on it is not seen through the factory's schema). Both libraries build their zod schemas with them, so importing a library builds none.
|
|
16
|
+
|
|
17
|
+
## 0.8.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- [#521](https://github.com/softistx/nxgt-material/pull/521) [`b76cd9a`](https://github.com/softistx/nxgt-material/commit/b76cd9ae3cc4f4d0f1b20cec31c9471819ffcd3c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Autocomplete` can search a server (softistx/nxgt-material#514). Fixes [#514](https://github.com/softistx/nxgt-material/issues/514).
|
|
22
|
+
|
|
23
|
+
- **Server search.** `onSearch(query, { signal })` (Vue: the function prop `on-search`, so `@search="fn"` binds it too) is called once typing pauses and its results become the list. Each search aborts the previous one (pass `signal` to `fetch`), and a response that lands after a newer one is dropped, even from an API that ignores the signal. `minQueryLength` (Vue: `min-query-length`, default 1) is how many characters it waits for; below it the list says "Type at least N characters to search." A rejected search is shown and announced ("The search failed. Try again.") and reported to `onSearchError` (Vue: `@search-error`). `options` is now optional: with `onSearch` it is the list before a search and the selected options' labels, and a picked option keeps its chip once a new search no longer returns it.
|
|
24
|
+
- **Controlled mode.** `loading` and `searchError` (Vue: `search-error`), beside `onQueryChange` (Vue: `@query-change`), for an app that fetches `options` itself.
|
|
25
|
+
- **A configurable debounce.** `debounceMs` (Vue: `debounce-ms`), 250 ms by default, sets the pause before `onSearch` and `onQueryChange`.
|
|
26
|
+
- **Accent-insensitive matching.** `filter`: `'accent-insensitive'` (the default without `onSearch`), `'none'` (the default with `onSearch`) or `(option, query) => boolean`. The default compares the label or value with the query lowercased in the provider's language and stripped of accents, so "e" finds "École" and "orle" finds "Orléans". Vue's exported `filterAutocompleteOptions` matches the same way and takes an optional `locale`.
|
|
27
|
+
- **Accessibility.** A polite live region announces "Loading…", then "N results" / "No results" (or the failure) once the search answers or typing pauses, not on every keystroke; the list is `aria-busy` while loading. New strings: `material.autocomplete.loading`, `.results`, `.search-error`, `.min-query` (en, fr). The keyboard works as before.
|
|
28
|
+
- React: clearing a single `Autocomplete`'s input no longer throws (`Cannot read properties of null (reading 'value')`).
|
|
29
|
+
- `@nxgt/material-core` adds `/debounce` (the `debounce` both libraries' `SearchField` kept a copy of, still exported by both, Vue now with its `Debounced` type), `/text-match` (`foldText`, `matchesText`) and `/async-search` (`createAsyncSearch`, `searchAnnouncement`).
|
|
30
|
+
|
|
31
|
+
Apps that built their own search field and result list while waiting for this (marked `Temporary, until material [#514](https://github.com/softistx/nxgt-material/issues/514)`) can replace it with `onSearch` and remove the workaround.
|
|
32
|
+
|
|
33
|
+
**Behaviour changes, no API removed.**
|
|
34
|
+
|
|
35
|
+
- Local matching ignores accents by default, so a query can keep more options than before. To match as before, pass your own `filter`, for example `filter={(option, query) => option.label.toLowerCase().includes(query.toLowerCase())}`; to show `options` exactly as given (filtered on your server), pass `filter="none"`.
|
|
36
|
+
- `onQueryChange` (`@query-change`) now runs 250 ms after typing pauses; it was a fixed 1 s in `@nxgt/material` and 300 ms in `@nxgt/material-vue`. Search for `onQueryChange` / `@query-change`, and add `debounceMs={1000}` (`:debounce-ms="300"`) where the old pause mattered — or move the fetch to `onSearch`.
|
|
37
|
+
- React: the list's empty text is no longer a live region of its own (Base UI's `ComboboxEmpty` had `role="status"`); the field's one status region (`[data-slot="autocomplete-status"]`) says "No results" instead. A test that found the empty text by `role="status"` finds it by its text.
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- [#519](https://github.com/softistx/nxgt-material/pull/519) [`18f30c1`](https://github.com/softistx/nxgt-material/commit/18f30c1cc6cc41c2b73a43fad2501b7db2bbc016) Thanks [@SteveGT96](https://github.com/SteveGT96)! - package.json names its repository, so issue reports route to it. Each package now has a `repository` field (`git+https://github.com/softistx/nxgt-material.git`, with its `directory` under `packages/`).
|
|
42
|
+
|
|
3
43
|
## 0.7.1
|
|
4
44
|
|
|
5
45
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -6,16 +6,18 @@ The framework-free core that [`@nxgt/material`](https://www.npmjs.com/package/@n
|
|
|
6
6
|
the print formatters, the PDF file helpers, the PDF viewer's framework-free
|
|
7
7
|
half (page geometry, scroll spy, text search), the currency field's decimals,
|
|
8
8
|
the file-size formatter, the slider's tick marks, the emoji list's groups,
|
|
9
|
-
the date range presets, the file preview's kind detection, the fields' blur
|
|
9
|
+
the date range presets, a debounce, accent-insensitive text matching and a race-free async search (`/debounce`, `/text-match`, `/async-search`), the file preview's kind detection, the fields' blur
|
|
10
10
|
rule, a field's first reachable control (`/focus-control`) and its `aria-describedby`
|
|
11
11
|
(`/described-by`), the page behind a modal layer made inert
|
|
12
12
|
(`/inert-others`), Escape pressed while an overlay closes
|
|
13
13
|
(`/escape-forward`), the Toaster's guard against a tap's emulated hover
|
|
14
14
|
(`/touch-hover`), the focus a toast's action hands back (`/toast-focus`), a
|
|
15
|
-
table row's activation, the zod error map's issue
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
table row's activation, the zod error map's issue messages
|
|
16
|
+
(`/zod-issue-messages`), the page's Content Security Policy nonce
|
|
17
|
+
(`/csp-nonce`), an object built on its first use (`/lazy-object`), the Vite
|
|
18
|
+
plugin that bundles pdfkit's standard fonts and a `build.assetsInlineLimit`
|
|
19
|
+
that keeps fonts out of `data:` URIs, and the Node-only resolution of those
|
|
20
|
+
fonts that the Next.js helper and the Nuxt module run at build time.
|
|
19
21
|
|
|
20
22
|
**You usually do not install it yourself.** Both libraries depend on it and
|
|
21
23
|
re-export what they always exported, at the same subpaths:
|
|
@@ -93,8 +95,13 @@ built-in.
|
|
|
93
95
|
| `/slider-ticks` | `tickValues` — where a slider's tick marks sit |
|
|
94
96
|
| `/emoji-groups` | `groupKey`, `groupEmojis`, `searchEmojis`, `groupName`, `GROUP_NAMES` and the type `EmojiItem` — how the emoji list groups, searches and names its emojis |
|
|
95
97
|
| `/date-range` | `DATE_RANGE_PRESET_IDS`, `presetRange`, `resolvePresetRange`, `isSameDayRange`, `isRangeDisabled`, `withTimeOf`, `endOfDayTime`, `clampRangeEnd`, `keepRangeTimes` and the types `DayRange`, `PartialDayRange`, `DateRangePreset`, `DateRangePresetId`, `DateRangePresetOption`, `DateRangeRules` — `DateRangeField`'s presets and times |
|
|
98
|
+
| `/csp-nonce` | `getCspNonce`, `setCspNonce`, `claimCspNonce(nonce, depth)`, `createNoncedStyle`, and `getNonce` / `setNonce` (the `get-nonce` API) — the page's Content Security Policy nonce, for a `<style>` created at runtime; browser only |
|
|
99
|
+
| `/lazy-object` | `lazyObject`, `buildOnce` — a value built on its first use rather than when its module loads: the libraries' zod schemas |
|
|
100
|
+
| `/debounce` | `debounce`, the type `Debounced` — a function called once calls pause, with `cancel` and `flush` (`SearchField`'s `debounceMs`) |
|
|
101
|
+
| `/text-match` | `foldText`, `matchesText` — case- and accent-insensitive matching (`'e'` finds "École"), lowercased for a locale |
|
|
102
|
+
| `/async-search` | `createAsyncSearch`, `searchAnnouncement`, `DEFAULT_SEARCH_DEBOUNCE_MS`, `DEFAULT_MIN_QUERY_LENGTH` and the types `AsyncSearch`, `AsyncSearchConfig`, `AsyncSearchState`, `AsyncSearchStatus`, `SearchFn`, `SearchContext`, `SearchAnnouncement` — `Autocomplete`'s server search: debounced, each run aborting the last, a stale response dropped |
|
|
96
103
|
| `/file-preview` | `fileKind`, `describeFile`, `safePreviewUrl`, `readTextPreview`, `FilePreviewHttpError`, `FilePreviewUnsafeUrlError`, `TEXT_PREVIEW_MAX_BYTES`, `fileExtension`, `fileNameFromUrl`, `dataUrlType`, `kindOfMimeType`, `normaliseMimeType`, `isTextFileKind`, `TEXT_FILE_KINDS` and the types `FileKind`, `FileKindInput`, `DescribedFile`, `FilePreviewSource`, `TextPreview` — `FilePreview`'s kind detection, URL allow-list and text reader |
|
|
97
|
-
| `/vite` | `pdfkitStandardFonts()` — see below |
|
|
104
|
+
| `/vite` | `pdfkitStandardFonts()`, `cspAssetsInlineLimit` — see below |
|
|
98
105
|
| `/pdfkit-fonts` | **Node only.** `pdfkitStandardFontDirs`, `pdfkitStandardFontFiles`, `PDFKIT_STANDARD_FONTS`, the types `PdfkitStandardFont` and `PdfkitFontsWarn` — where the app's pdfkit keeps its standard fonts, for a server tracer |
|
|
99
106
|
|
|
100
107
|
## Usage
|
|
@@ -375,6 +382,81 @@ tickValues(0, 100, true, 1).length; // 21 — capped
|
|
|
375
382
|
tickValues(5, 5, true); // [5] — min === max
|
|
376
383
|
```
|
|
377
384
|
|
|
385
|
+
### A debounced call: `/debounce`
|
|
386
|
+
|
|
387
|
+
The debounce both libraries' `SearchField` (`debounceMs`) and `Autocomplete`
|
|
388
|
+
use: the wrapped function runs `ms` after the last call, with the last
|
|
389
|
+
arguments. `cancel()` drops a pending call, `flush()` runs it now.
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
import { debounce } from '@nxgt/material-core/debounce';
|
|
393
|
+
|
|
394
|
+
const save = debounce((text: string) => console.log('save', text), 300);
|
|
395
|
+
save('a');
|
|
396
|
+
save('ab'); // 300 ms later: "save ab", once
|
|
397
|
+
save.flush(); // or now
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Matching text as the user types: `/text-match`
|
|
401
|
+
|
|
402
|
+
`foldText(text, locale?)` lowercases `text` for `locale` (the runtime's when
|
|
403
|
+
omitted; an invalid tag falls back to it), then strips its diacritics
|
|
404
|
+
(`normalize('NFD')`, combining marks removed). `matchesText(text, query,
|
|
405
|
+
locale?)` says whether the folded, trimmed query occurs in the folded text;
|
|
406
|
+
a blank query matches everything. Both libraries' `Autocomplete` filters its
|
|
407
|
+
local options with it, in the provider's language.
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
import { foldText, matchesText } from '@nxgt/material-core/text-match';
|
|
411
|
+
|
|
412
|
+
foldText('Élève'); // 'eleve'
|
|
413
|
+
foldText('İSTANBUL', 'tr'); // 'istanbul'
|
|
414
|
+
matchesText('Orléans', 'orle'); // true
|
|
415
|
+
matchesText('Cafe', 'café'); // true — both ways
|
|
416
|
+
matchesText('Lyon', ' '); // true — a blank query keeps everything
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Letters that are not a base letter plus a mark stay as they are: `ß`, `ø`
|
|
420
|
+
and `æ` do not fold to `ss`, `o` or `ae`.
|
|
421
|
+
|
|
422
|
+
### A search that never answers out of order: `/async-search`
|
|
423
|
+
|
|
424
|
+
`createAsyncSearch(config, onChange)` runs `config.search(query, { signal })`
|
|
425
|
+
once `request(query)` calls pause for `debounceMs` (250 by default). Each run
|
|
426
|
+
aborts the one before it, and a response that still lands for an older run
|
|
427
|
+
is dropped, so a slow first query never overwrites a faster second one; an
|
|
428
|
+
abort is never reported as an error. A query shorter than `minQueryLength`
|
|
429
|
+
(1 by default, trimmed) searches nothing and resets the state to `idle`.
|
|
430
|
+
`onChange` receives each state: `{ status: 'idle' | 'loading' | 'success' |
|
|
431
|
+
'error', query, results, error? }`, the last results kept while the next
|
|
432
|
+
query loads.
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
import { createAsyncSearch } from '@nxgt/material-core/async-search';
|
|
436
|
+
|
|
437
|
+
type City = { value: string; label: string };
|
|
438
|
+
|
|
439
|
+
const search = createAsyncSearch<City>(
|
|
440
|
+
{
|
|
441
|
+
search: (query, { signal }) =>
|
|
442
|
+
fetch(`/api/cities?q=${encodeURIComponent(query)}`, { signal }).then((r) => r.json()),
|
|
443
|
+
minQueryLength: 2,
|
|
444
|
+
},
|
|
445
|
+
(state) => render(state),
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
input.addEventListener('input', () => search.request(input.value));
|
|
449
|
+
// On teardown: search.cancel() drops the pending search and aborts the one in flight.
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
`configure(partial)` swaps the search, the debounce or the minimum length
|
|
453
|
+
(React re-reads the latest props through it), `flush()` runs the pending
|
|
454
|
+
search now, and requesting the query the state already holds (or is loading)
|
|
455
|
+
does nothing. `searchAnnouncement({ loading, failed, settled, count })` is
|
|
456
|
+
what a combobox's polite live region says: the failure, else "loading",
|
|
457
|
+
else the count once the query has settled — `null` while the user is still
|
|
458
|
+
typing.
|
|
459
|
+
|
|
378
460
|
### The emoji list's groups: `/emoji-groups`
|
|
379
461
|
|
|
380
462
|
How both libraries' `EmojiList` groups, searches and names its emojis:
|
|
@@ -430,6 +512,81 @@ the end;
|
|
|
430
512
|
`withTimeOf(day, time)` and `endOfDayTime(day)` set one end's time;
|
|
431
513
|
`isSameDayRange(a, b)` compares two ranges by their days.
|
|
432
514
|
|
|
515
|
+
### The page's CSP nonce: `/csp-nonce`
|
|
516
|
+
|
|
517
|
+
Under a strict Content Security Policy (`style-src-elem 'nonce-X'`), a
|
|
518
|
+
`<style>` element created at runtime is blocked unless it carries the page's
|
|
519
|
+
nonce. `getCspNonce()` finds it, from the first of: the value given to
|
|
520
|
+
`setCspNonce`, `<meta property="csp-nonce" nonce="…">` (what Vite's
|
|
521
|
+
`html.cspNonce` writes) or `<meta name="csp-nonce" content="…">`, the first
|
|
522
|
+
`<script nonce>` or `<style nonce>` of the page, then
|
|
523
|
+
`globalThis.__webpack_nonce__`. It reads an element's `nonce` property,
|
|
524
|
+
since browsers hide the attribute's value once the page is parsed.
|
|
525
|
+
`createNoncedStyle()` returns a new, unattached `<style>` that carries it.
|
|
526
|
+
|
|
527
|
+
```ts
|
|
528
|
+
import { createNoncedStyle, setCspNonce } from '@nxgt/material-core/csp-nonce';
|
|
529
|
+
|
|
530
|
+
// Nothing to set when the page carries Vite's tag (html.cspNonce):
|
|
531
|
+
// <meta property="csp-nonce" nonce="…">
|
|
532
|
+
// With a server-rendered nonce, set it in entry.client.ts before the first
|
|
533
|
+
// component renders:
|
|
534
|
+
setCspNonce(nonceFromTheServer);
|
|
535
|
+
|
|
536
|
+
const style = createNoncedStyle();
|
|
537
|
+
style.textContent = '.spin { animation: spin 1s linear infinite }';
|
|
538
|
+
document.head.append(style);
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
- A `content` attribute is not hidden from CSS selectors, so prefer the
|
|
542
|
+
`nonce` attribute.
|
|
543
|
+
- **Browser only.** Without a `document` (a server), `setCspNonce` does
|
|
544
|
+
nothing and `getCspNonce` returns `undefined`: a nonce belongs to one
|
|
545
|
+
request, and a module global would hand it to the next. Pass it down the
|
|
546
|
+
request instead. `createNoncedStyle()` with no `document` needs an explicit
|
|
547
|
+
`doc` argument, otherwise it throws. An SSR setup that installs a global
|
|
548
|
+
`document` (jsdom or domino) makes `setCspNonce` a global shared by every
|
|
549
|
+
request, so do not call it on the server.
|
|
550
|
+
- The value is kept on `globalThis` under `Symbol.for('nxgt.cspNonce')`, so
|
|
551
|
+
two copies of this package in one bundle share it.
|
|
552
|
+
- **For a provider component** (the libraries' `NonceProvider`): call
|
|
553
|
+
`claimCspNonce(nonce, depth)` once mounted, with how many providers enclose
|
|
554
|
+
it, and the function it returns on unmount. The deepest claim held is the
|
|
555
|
+
stored nonce, so nested providers stay right, and releasing the last one
|
|
556
|
+
puts back what the app had set with `setCspNonce`.
|
|
557
|
+
- `getNonce` and `setNonce` are the same two functions under
|
|
558
|
+
[`get-nonce`](https://www.npmjs.com/package/get-nonce)'s names, so a
|
|
559
|
+
bundler alias can point that package here.
|
|
560
|
+
|
|
561
|
+
### Built on first use: `/lazy-object`
|
|
562
|
+
|
|
563
|
+
`buildOnce(build)` returns a function that calls `build` on its first call
|
|
564
|
+
and returns that same value after. `lazyObject(build)` returns a stand-in
|
|
565
|
+
for `build`'s object that builds it on the first operation of any kind
|
|
566
|
+
(a property read, a method call, `in`, `Object.keys`, `instanceof`), then
|
|
567
|
+
answers as that object does. Both libraries build their zod schemas this
|
|
568
|
+
way, so importing them builds none: building a `z.object()` makes zod probe
|
|
569
|
+
`new Function`, which a strict Content Security Policy reports, and the app
|
|
570
|
+
must be able to call `z.config({ jitless: true })` first.
|
|
571
|
+
|
|
572
|
+
```ts
|
|
573
|
+
import { buildOnce, lazyObject } from '@nxgt/material-core/lazy-object';
|
|
574
|
+
import { z } from 'zod';
|
|
575
|
+
|
|
576
|
+
export const userSchema = buildOnce(() => z.object({ name: z.string() }));
|
|
577
|
+
|
|
578
|
+
/** @deprecated Use `userSchema()`. */
|
|
579
|
+
export const UserSchema = lazyObject(userSchema);
|
|
580
|
+
|
|
581
|
+
userSchema() === userSchema(); // true: built once
|
|
582
|
+
UserSchema.parse({ name: 'Ada' }); // builds it here, then parses
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
- The stand-in is not the built object: `UserSchema === userSchema()` is
|
|
586
|
+
`false`. Use it for a value its callers read, never one they compare by
|
|
587
|
+
identity. A property written on it lands on the stand-in's copy.
|
|
588
|
+
- Nothing here imports zod: any object works.
|
|
589
|
+
|
|
433
590
|
### A file's preview: `/file-preview`
|
|
434
591
|
|
|
435
592
|
The pure half of both libraries' `FilePreview`. `fileKind` picks the kind of
|
|
@@ -702,6 +859,24 @@ export default defineConfig({ plugins: [pdfkitStandardFonts()] });
|
|
|
702
859
|
function. The doc comment on `pdfkitStandardFonts` explains when you need it
|
|
703
860
|
and when keeping the library external is the lighter answer.
|
|
704
861
|
|
|
862
|
+
### Fonts under a strict CSP: `cspAssetsInlineLimit` (`/vite`)
|
|
863
|
+
|
|
864
|
+
Vite inlines an asset under 4096 bytes as a `data:` URI, and `font-src
|
|
865
|
+
'self'` blocks a font served that way (KaTeX's `KaTeX_Size3-Regular.woff2`
|
|
866
|
+
is 3.6 KB). `cspAssetsInlineLimit` never inlines a font (`.woff`, `.woff2`,
|
|
867
|
+
`.ttf`, `.otf`, `.eot`) and leaves every other asset to Vite's default:
|
|
868
|
+
|
|
869
|
+
```ts
|
|
870
|
+
// vite.config.ts
|
|
871
|
+
import { cspAssetsInlineLimit } from '@nxgt/material-core/vite';
|
|
872
|
+
import { defineConfig } from 'vite';
|
|
873
|
+
|
|
874
|
+
export default defineConfig({ build: { assetsInlineLimit: cspAssetsInlineLimit } });
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
To keep more files out, wrap it:
|
|
878
|
+
`(file, content) => (file.endsWith('.svg') ? false : cspAssetsInlineLimit(file, content))`.
|
|
879
|
+
|
|
705
880
|
### Tracing pdfkit's fonts: `/pdfkit-fonts` (Node only)
|
|
706
881
|
|
|
707
882
|
A server that keeps `node_modules` but copies only what a tracer finds
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/lib/debounce.ts
|
|
2
|
+
function debounce2(fn, ms) {
|
|
3
|
+
let timer;
|
|
4
|
+
let lastArgs;
|
|
5
|
+
const run = () => {
|
|
6
|
+
timer = undefined;
|
|
7
|
+
const pending = lastArgs;
|
|
8
|
+
lastArgs = undefined;
|
|
9
|
+
if (pending)
|
|
10
|
+
fn(...pending);
|
|
11
|
+
};
|
|
12
|
+
const wrapped = (...args) => {
|
|
13
|
+
lastArgs = args;
|
|
14
|
+
if (timer !== undefined)
|
|
15
|
+
clearTimeout(timer);
|
|
16
|
+
timer = setTimeout(run, ms);
|
|
17
|
+
};
|
|
18
|
+
wrapped.cancel = () => {
|
|
19
|
+
if (timer !== undefined) {
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
timer = undefined;
|
|
22
|
+
}
|
|
23
|
+
lastArgs = undefined;
|
|
24
|
+
};
|
|
25
|
+
wrapped.flush = () => {
|
|
26
|
+
if (timer === undefined)
|
|
27
|
+
return;
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
run();
|
|
30
|
+
};
|
|
31
|
+
return wrapped;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { debounce2 };
|
|
35
|
+
|
|
36
|
+
//# debugId=863EA47559EC981164756E2164756E21
|
|
37
|
+
//# sourceMappingURL=index-f24x24a8.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/debounce.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"export type Debounced<Args extends unknown[]> = ((...args: Args) => void) & {\n\tcancel: () => void;\n\tflush: () => void;\n};\n\nexport function debounce<Args extends unknown[]>(\n\tfn: (...args: Args) => void,\n\tms: number,\n): Debounced<Args> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet lastArgs: Args | undefined;\n\n\tconst run = () => {\n\t\ttimer = undefined;\n\t\tconst pending = lastArgs;\n\t\tlastArgs = undefined;\n\t\tif (pending) fn(...pending);\n\t};\n\n\tconst wrapped = ((...args: Args) => {\n\t\tlastArgs = args;\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = setTimeout(run, ms);\n\t}) as Debounced<Args>;\n\n\twrapped.cancel = () => {\n\t\tif (timer !== undefined) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t\tlastArgs = undefined;\n\t};\n\n\twrapped.flush = () => {\n\t\tif (timer === undefined) return;\n\t\tclearTimeout(timer);\n\t\trun();\n\t};\n\n\treturn wrapped;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAKO,SAAS,SAAgC,CAC/C,IACA,IACkB;AAAA,EAClB,IAAI;AAAA,EACJ,IAAI;AAAA,EAEJ,MAAM,MAAM,MAAM;AAAA,IACjB,QAAQ;AAAA,IACR,MAAM,UAAU;AAAA,IAChB,WAAW;AAAA,IACX,IAAI;AAAA,MAAS,GAAG,GAAG,OAAO;AAAA;AAAA,EAG3B,MAAM,UAAW,IAAI,SAAe;AAAA,IACnC,WAAW;AAAA,IACX,IAAI,UAAU;AAAA,MAAW,aAAa,KAAK;AAAA,IAC3C,QAAQ,WAAW,KAAK,EAAE;AAAA;AAAA,EAG3B,QAAQ,SAAS,MAAM;AAAA,IACtB,IAAI,UAAU,WAAW;AAAA,MACxB,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,IACT;AAAA,IACA,WAAW;AAAA;AAAA,EAGZ,QAAQ,QAAQ,MAAM;AAAA,IACrB,IAAI,UAAU;AAAA,MAAW;AAAA,IACzB,aAAa,KAAK;AAAA,IAClB,IAAI;AAAA;AAAA,EAGL,OAAO;AAAA;",
|
|
8
|
+
"debugId": "863EA47559EC981164756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export { type SearchAnnouncement, searchAnnouncement, } from './search-announcement';
|
|
2
|
+
/** What a search is handed besides the query: aborted once it is stale. */
|
|
3
|
+
export type SearchContext = {
|
|
4
|
+
signal: AbortSignal;
|
|
5
|
+
};
|
|
6
|
+
/** A server search: the options for `query`. Pass `signal` on to `fetch`. */
|
|
7
|
+
export type SearchFn<T> = (query: string, context: SearchContext) => Promise<T[]>;
|
|
8
|
+
export type AsyncSearchStatus = 'idle' | 'loading' | 'success' | 'error';
|
|
9
|
+
export type AsyncSearchState<T> = {
|
|
10
|
+
status: AsyncSearchStatus;
|
|
11
|
+
/** The query being searched (loading) or the one `results` answer. */
|
|
12
|
+
query: string;
|
|
13
|
+
/** The last results. Kept while the next query loads; `[]` when idle. */
|
|
14
|
+
results: T[];
|
|
15
|
+
/** What the search rejected with (`status: 'error'`). */
|
|
16
|
+
error?: unknown;
|
|
17
|
+
};
|
|
18
|
+
export type AsyncSearchConfig<T> = {
|
|
19
|
+
search: SearchFn<T>;
|
|
20
|
+
/** How long typing must pause before a search runs. Default 250. */
|
|
21
|
+
debounceMs?: number;
|
|
22
|
+
/** Below this many characters (trimmed) nothing is searched. Default 1. */
|
|
23
|
+
minQueryLength?: number;
|
|
24
|
+
};
|
|
25
|
+
export type AsyncSearch<T> = {
|
|
26
|
+
/** The current state; `onChange` receives each new one. */
|
|
27
|
+
readonly state: AsyncSearchState<T>;
|
|
28
|
+
/** Search `query` (trimmed) once typing pauses; a short one resets to idle at once. */
|
|
29
|
+
request: (query: string) => void;
|
|
30
|
+
/** Run the pending search now. */
|
|
31
|
+
flush: () => void;
|
|
32
|
+
/** Drop the pending search and abort the one in flight. */
|
|
33
|
+
cancel: () => void;
|
|
34
|
+
/** Swap the search, the debounce or the minimum length. */
|
|
35
|
+
configure: (config: Partial<AsyncSearchConfig<T>>) => void;
|
|
36
|
+
};
|
|
37
|
+
export declare const DEFAULT_SEARCH_DEBOUNCE_MS = 250;
|
|
38
|
+
export declare const DEFAULT_MIN_QUERY_LENGTH = 1;
|
|
39
|
+
/**
|
|
40
|
+
* A debounced search whose responses can never arrive out of order: each run
|
|
41
|
+
* aborts the one before it (its `signal`), and a response that still lands
|
|
42
|
+
* for an older run is dropped, so a slow first query never overwrites the
|
|
43
|
+
* results of a faster second one. An abort is not an error.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createAsyncSearch<T>(config: AsyncSearchConfig<T>, onChange: (state: AsyncSearchState<T>) => void): AsyncSearch<T>;
|
|
46
|
+
//# sourceMappingURL=async-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-search.d.ts","sourceRoot":"","sources":["../../src/lib/async-search.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,kBAAkB,EACvB,kBAAkB,GAClB,MAAM,uBAAuB,CAAC;AAE/B,2EAA2E;AAC3E,MAAM,MAAM,aAAa,GAAG;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC;AAEpD,6EAA6E;AAC7E,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CACzB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,KAClB,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;AAElB,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEzE,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IACjC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IAClC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IACpC,uFAAuF;IACvF,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,kCAAkC;IAClC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,2DAA2D;IAC3D,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CAC3D,CAAC;AAEF,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAQ1C;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAClC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC5B,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,IAAI,GAC5C,WAAW,CAAC,CAAC,CAAC,CAEhB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
debounce2
|
|
3
|
+
} from "../chunks/index-f24x24a8.js";
|
|
4
|
+
|
|
5
|
+
// src/lib/search-announcement.ts
|
|
6
|
+
function searchAnnouncement(list) {
|
|
7
|
+
if (list.failed)
|
|
8
|
+
return { kind: "error" };
|
|
9
|
+
if (list.loading)
|
|
10
|
+
return { kind: "loading" };
|
|
11
|
+
return list.settled ? { kind: "results", count: list.count } : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/lib/async-search.ts
|
|
15
|
+
var DEFAULT_SEARCH_DEBOUNCE_MS = 250;
|
|
16
|
+
var DEFAULT_MIN_QUERY_LENGTH = 1;
|
|
17
|
+
var idle = () => ({
|
|
18
|
+
status: "idle",
|
|
19
|
+
query: "",
|
|
20
|
+
results: []
|
|
21
|
+
});
|
|
22
|
+
function createAsyncSearch(config, onChange) {
|
|
23
|
+
return new AsyncSearchRunner(config, onChange);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class AsyncSearchRunner {
|
|
27
|
+
#config;
|
|
28
|
+
#onChange;
|
|
29
|
+
#state;
|
|
30
|
+
#controller;
|
|
31
|
+
#ticket;
|
|
32
|
+
#pending;
|
|
33
|
+
#pendingMs;
|
|
34
|
+
constructor(config, onChange) {
|
|
35
|
+
this.#state = idle();
|
|
36
|
+
this.#ticket = 0;
|
|
37
|
+
this.#config = config;
|
|
38
|
+
this.#onChange = onChange;
|
|
39
|
+
}
|
|
40
|
+
get state() {
|
|
41
|
+
return this.#state;
|
|
42
|
+
}
|
|
43
|
+
request(text) {
|
|
44
|
+
const query = text.trim();
|
|
45
|
+
const min = this.#config.minQueryLength ?? DEFAULT_MIN_QUERY_LENGTH;
|
|
46
|
+
if (query.length < min) {
|
|
47
|
+
this.cancel();
|
|
48
|
+
if (this.#state.status !== "idle")
|
|
49
|
+
this.#set(idle());
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const { status } = this.#state;
|
|
53
|
+
if ((status === "loading" || status === "success") && this.#state.query === query) {
|
|
54
|
+
this.#pending?.cancel();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.#debounced()(query);
|
|
58
|
+
}
|
|
59
|
+
flush() {
|
|
60
|
+
this.#pending?.flush();
|
|
61
|
+
}
|
|
62
|
+
cancel() {
|
|
63
|
+
this.#pending?.cancel();
|
|
64
|
+
this.#abort();
|
|
65
|
+
if (this.#state.status === "loading")
|
|
66
|
+
this.#set(idle());
|
|
67
|
+
}
|
|
68
|
+
configure(config) {
|
|
69
|
+
this.#config = { ...this.#config, ...config };
|
|
70
|
+
}
|
|
71
|
+
#set(next) {
|
|
72
|
+
this.#state = next;
|
|
73
|
+
this.#onChange(next);
|
|
74
|
+
}
|
|
75
|
+
#abort() {
|
|
76
|
+
this.#ticket += 1;
|
|
77
|
+
this.#controller?.abort();
|
|
78
|
+
this.#controller = undefined;
|
|
79
|
+
}
|
|
80
|
+
#debounced() {
|
|
81
|
+
const ms = this.#config.debounceMs ?? DEFAULT_SEARCH_DEBOUNCE_MS;
|
|
82
|
+
if (!this.#pending || this.#pendingMs !== ms) {
|
|
83
|
+
this.#pending?.cancel();
|
|
84
|
+
this.#pending = debounce2((query) => void this.#run(query), ms);
|
|
85
|
+
this.#pendingMs = ms;
|
|
86
|
+
}
|
|
87
|
+
return this.#pending;
|
|
88
|
+
}
|
|
89
|
+
async#run(query) {
|
|
90
|
+
this.#abort();
|
|
91
|
+
const own = new AbortController;
|
|
92
|
+
this.#controller = own;
|
|
93
|
+
const ticket = this.#ticket;
|
|
94
|
+
this.#set({ status: "loading", query, results: this.#state.results });
|
|
95
|
+
try {
|
|
96
|
+
const results = await this.#config.search(query, { signal: own.signal });
|
|
97
|
+
if (ticket === this.#ticket)
|
|
98
|
+
this.#set({ status: "success", query, results });
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (ticket === this.#ticket && !own.signal.aborted)
|
|
101
|
+
this.#set({ status: "error", query, results: [], error });
|
|
102
|
+
} finally {
|
|
103
|
+
if (this.#controller === own)
|
|
104
|
+
this.#controller = undefined;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export {
|
|
109
|
+
DEFAULT_MIN_QUERY_LENGTH,
|
|
110
|
+
DEFAULT_SEARCH_DEBOUNCE_MS,
|
|
111
|
+
createAsyncSearch,
|
|
112
|
+
searchAnnouncement
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
//# debugId=DF9F6FB2BDDD168C64756E2164756E21
|
|
116
|
+
//# sourceMappingURL=async-search.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/search-announcement.ts", "../src/lib/async-search.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/** What a combobox's polite live region says about its list, if anything. */\nexport type SearchAnnouncement =\n\t| { kind: 'loading' }\n\t| { kind: 'error' }\n\t| { kind: 'results'; count: number };\n\n/**\n * The announcement for a list: its failure, else that it is loading, else\n * its count once the query has `settled` (searched, or typing paused) —\n * `null` while the user is still typing, so a screen reader is not told\n * about every keystroke.\n */\nexport function searchAnnouncement(list: {\n\tloading: boolean;\n\tfailed: boolean;\n\tsettled: boolean;\n\tcount: number;\n}): SearchAnnouncement | null {\n\tif (list.failed) return { kind: 'error' };\n\tif (list.loading) return { kind: 'loading' };\n\treturn list.settled ? { kind: 'results', count: list.count } : null;\n}\n",
|
|
6
|
+
"import { type Debounced, debounce } from './debounce';\n\nexport {\n\ttype SearchAnnouncement,\n\tsearchAnnouncement,\n} from './search-announcement';\n\n/** What a search is handed besides the query: aborted once it is stale. */\nexport type SearchContext = { signal: AbortSignal };\n\n/** A server search: the options for `query`. Pass `signal` on to `fetch`. */\nexport type SearchFn<T> = (\n\tquery: string,\n\tcontext: SearchContext,\n) => Promise<T[]>;\n\nexport type AsyncSearchStatus = 'idle' | 'loading' | 'success' | 'error';\n\nexport type AsyncSearchState<T> = {\n\tstatus: AsyncSearchStatus;\n\t/** The query being searched (loading) or the one `results` answer. */\n\tquery: string;\n\t/** The last results. Kept while the next query loads; `[]` when idle. */\n\tresults: T[];\n\t/** What the search rejected with (`status: 'error'`). */\n\terror?: unknown;\n};\n\nexport type AsyncSearchConfig<T> = {\n\tsearch: SearchFn<T>;\n\t/** How long typing must pause before a search runs. Default 250. */\n\tdebounceMs?: number;\n\t/** Below this many characters (trimmed) nothing is searched. Default 1. */\n\tminQueryLength?: number;\n};\n\nexport type AsyncSearch<T> = {\n\t/** The current state; `onChange` receives each new one. */\n\treadonly state: AsyncSearchState<T>;\n\t/** Search `query` (trimmed) once typing pauses; a short one resets to idle at once. */\n\trequest: (query: string) => void;\n\t/** Run the pending search now. */\n\tflush: () => void;\n\t/** Drop the pending search and abort the one in flight. */\n\tcancel: () => void;\n\t/** Swap the search, the debounce or the minimum length. */\n\tconfigure: (config: Partial<AsyncSearchConfig<T>>) => void;\n};\n\nexport const DEFAULT_SEARCH_DEBOUNCE_MS = 250;\nexport const DEFAULT_MIN_QUERY_LENGTH = 1;\n\nconst idle = <T>(): AsyncSearchState<T> => ({\n\tstatus: 'idle',\n\tquery: '',\n\tresults: [],\n});\n\n/**\n * A debounced search whose responses can never arrive out of order: each run\n * aborts the one before it (its `signal`), and a response that still lands\n * for an older run is dropped, so a slow first query never overwrites the\n * results of a faster second one. An abort is not an error.\n */\nexport function createAsyncSearch<T>(\n\tconfig: AsyncSearchConfig<T>,\n\tonChange: (state: AsyncSearchState<T>) => void,\n): AsyncSearch<T> {\n\treturn new AsyncSearchRunner(config, onChange);\n}\n\nclass AsyncSearchRunner<T> implements AsyncSearch<T> {\n\t#config: AsyncSearchConfig<T>;\n\t#onChange: (state: AsyncSearchState<T>) => void;\n\t#state: AsyncSearchState<T> = idle();\n\t#controller: AbortController | undefined;\n\t/** Bumped by every run and every abort: a response for an older one is stale. */\n\t#ticket = 0;\n\t#pending: Debounced<[string]> | undefined;\n\t#pendingMs: number | undefined;\n\n\tconstructor(\n\t\tconfig: AsyncSearchConfig<T>,\n\t\tonChange: (state: AsyncSearchState<T>) => void,\n\t) {\n\t\tthis.#config = config;\n\t\tthis.#onChange = onChange;\n\t}\n\n\tget state() {\n\t\treturn this.#state;\n\t}\n\n\trequest(text: string) {\n\t\t// \"pa\" and \"pa \" are one search, and the server never sees the space.\n\t\tconst query = text.trim();\n\t\tconst min = this.#config.minQueryLength ?? DEFAULT_MIN_QUERY_LENGTH;\n\t\tif (query.length < min) {\n\t\t\tthis.cancel();\n\t\t\tif (this.#state.status !== 'idle') this.#set(idle());\n\t\t\treturn;\n\t\t}\n\t\tconst { status } = this.#state;\n\t\tif (\n\t\t\t(status === 'loading' || status === 'success') &&\n\t\t\tthis.#state.query === query\n\t\t) {\n\t\t\tthis.#pending?.cancel();\n\t\t\treturn;\n\t\t}\n\t\tthis.#debounced()(query);\n\t}\n\n\tflush() {\n\t\tthis.#pending?.flush();\n\t}\n\n\tcancel() {\n\t\tthis.#pending?.cancel();\n\t\tthis.#abort();\n\t\tif (this.#state.status === 'loading') this.#set(idle());\n\t}\n\n\tconfigure(config: Partial<AsyncSearchConfig<T>>) {\n\t\tthis.#config = { ...this.#config, ...config };\n\t}\n\n\t#set(next: AsyncSearchState<T>) {\n\t\tthis.#state = next;\n\t\tthis.#onChange(next);\n\t}\n\n\t#abort() {\n\t\tthis.#ticket += 1;\n\t\tthis.#controller?.abort();\n\t\tthis.#controller = undefined;\n\t}\n\n\t#debounced() {\n\t\tconst ms = this.#config.debounceMs ?? DEFAULT_SEARCH_DEBOUNCE_MS;\n\t\tif (!this.#pending || this.#pendingMs !== ms) {\n\t\t\tthis.#pending?.cancel();\n\t\t\tthis.#pending = debounce((query: string) => void this.#run(query), ms);\n\t\t\tthis.#pendingMs = ms;\n\t\t}\n\t\treturn this.#pending;\n\t}\n\n\tasync #run(query: string) {\n\t\tthis.#abort();\n\t\tconst own = new AbortController();\n\t\tthis.#controller = own;\n\t\tconst ticket = this.#ticket;\n\t\tthis.#set({ status: 'loading', query, results: this.#state.results });\n\t\ttry {\n\t\t\tconst results = await this.#config.search(query, { signal: own.signal });\n\t\t\tif (ticket === this.#ticket)\n\t\t\t\tthis.#set({ status: 'success', query, results });\n\t\t} catch (error) {\n\t\t\tif (ticket === this.#ticket && !own.signal.aborted)\n\t\t\t\tthis.#set({ status: 'error', query, results: [], error });\n\t\t} finally {\n\t\t\tif (this.#controller === own) this.#controller = undefined;\n\t\t}\n\t}\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": ";;;;;AAYO,SAAS,kBAAkB,CAAC,MAKL;AAAA,EAC7B,IAAI,KAAK;AAAA,IAAQ,OAAO,EAAE,MAAM,QAAQ;AAAA,EACxC,IAAI,KAAK;AAAA,IAAS,OAAO,EAAE,MAAM,UAAU;AAAA,EAC3C,OAAO,KAAK,UAAU,EAAE,MAAM,WAAW,OAAO,KAAK,MAAM,IAAI;AAAA;;;AC6BzD,IAAM,6BAA6B;AACnC,IAAM,2BAA2B;AAExC,IAAM,OAAO,OAA+B;AAAA,EAC3C,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS,CAAC;AACX;AAQO,SAAS,iBAAoB,CACnC,QACA,UACiB;AAAA,EACjB,OAAO,IAAI,kBAAkB,QAAQ,QAAQ;AAAA;AAAA;AAG9C,MAAM,kBAA+C;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,WAAW,CACV,QACA,UACC;AAAA,IAVF,cAA8B,KAAK;AAAA,IAGnC,eAAU;AAAA,IAQT,KAAK,UAAU;AAAA,IACf,KAAK,YAAY;AAAA;AAAA,MAGd,KAAK,GAAG;AAAA,IACX,OAAO,KAAK;AAAA;AAAA,EAGb,OAAO,CAAC,MAAc;AAAA,IAErB,MAAM,QAAQ,KAAK,KAAK;AAAA,IACxB,MAAM,MAAM,KAAK,QAAQ,kBAAkB;AAAA,IAC3C,IAAI,MAAM,SAAS,KAAK;AAAA,MACvB,KAAK,OAAO;AAAA,MACZ,IAAI,KAAK,OAAO,WAAW;AAAA,QAAQ,KAAK,KAAK,KAAK,CAAC;AAAA,MACnD;AAAA,IACD;AAAA,IACA,QAAQ,WAAW,KAAK;AAAA,IACxB,KACE,WAAW,aAAa,WAAW,cACpC,KAAK,OAAO,UAAU,OACrB;AAAA,MACD,KAAK,UAAU,OAAO;AAAA,MACtB;AAAA,IACD;AAAA,IACA,KAAK,WAAW,EAAE,KAAK;AAAA;AAAA,EAGxB,KAAK,GAAG;AAAA,IACP,KAAK,UAAU,MAAM;AAAA;AAAA,EAGtB,MAAM,GAAG;AAAA,IACR,KAAK,UAAU,OAAO;AAAA,IACtB,KAAK,OAAO;AAAA,IACZ,IAAI,KAAK,OAAO,WAAW;AAAA,MAAW,KAAK,KAAK,KAAK,CAAC;AAAA;AAAA,EAGvD,SAAS,CAAC,QAAuC;AAAA,IAChD,KAAK,UAAU,KAAK,KAAK,YAAY,OAAO;AAAA;AAAA,EAG7C,IAAI,CAAC,MAA2B;AAAA,IAC/B,KAAK,SAAS;AAAA,IACd,KAAK,UAAU,IAAI;AAAA;AAAA,EAGpB,MAAM,GAAG;AAAA,IACR,KAAK,WAAW;AAAA,IAChB,KAAK,aAAa,MAAM;AAAA,IACxB,KAAK,cAAc;AAAA;AAAA,EAGpB,UAAU,GAAG;AAAA,IACZ,MAAM,KAAK,KAAK,QAAQ,cAAc;AAAA,IACtC,IAAI,CAAC,KAAK,YAAY,KAAK,eAAe,IAAI;AAAA,MAC7C,KAAK,UAAU,OAAO;AAAA,MACtB,KAAK,WAAW,UAAS,CAAC,UAAkB,KAAK,KAAK,KAAK,KAAK,GAAG,EAAE;AAAA,MACrE,KAAK,aAAa;AAAA,IACnB;AAAA,IACA,OAAO,KAAK;AAAA;AAAA,OAGP,IAAI,CAAC,OAAe;AAAA,IACzB,KAAK,OAAO;AAAA,IACZ,MAAM,MAAM,IAAI;AAAA,IAChB,KAAK,cAAc;AAAA,IACnB,MAAM,SAAS,KAAK;AAAA,IACpB,KAAK,KAAK,EAAE,QAAQ,WAAW,OAAO,SAAS,KAAK,OAAO,QAAQ,CAAC;AAAA,IACpE,IAAI;AAAA,MACH,MAAM,UAAU,MAAM,KAAK,QAAQ,OAAO,OAAO,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,MACvE,IAAI,WAAW,KAAK;AAAA,QACnB,KAAK,KAAK,EAAE,QAAQ,WAAW,OAAO,QAAQ,CAAC;AAAA,MAC/C,OAAO,OAAO;AAAA,MACf,IAAI,WAAW,KAAK,WAAW,CAAC,IAAI,OAAO;AAAA,QAC1C,KAAK,KAAK,EAAE,QAAQ,SAAS,OAAO,SAAS,CAAC,GAAG,MAAM,CAAC;AAAA,cACxD;AAAA,MACD,IAAI,KAAK,gBAAgB;AAAA,QAAK,KAAK,cAAc;AAAA;AAAA;AAGpD;",
|
|
9
|
+
"debugId": "DF9F6FB2BDDD168C64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page's Content Security Policy nonce, for the code that creates a
|
|
3
|
+
* `<style>` (or `<script>`) element at runtime: a strict `style-src-elem
|
|
4
|
+
* 'nonce-X'` blocks any element that does not carry it.
|
|
5
|
+
*
|
|
6
|
+
* Browser only. On a server the nonce belongs to one request, so it is never
|
|
7
|
+
* kept in a module global there: `setCspNonce` does nothing without a
|
|
8
|
+
* `document`, and `getCspNonce` then answers `undefined`. Pass the nonce down
|
|
9
|
+
* the request instead (an argument, a context, `AsyncLocalStorage`).
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Keeps `nonce` for the page, read first by `getCspNonce`. `undefined` (or
|
|
13
|
+
* `''`) forgets it. A no-op on a server (no `document`), so a request's nonce
|
|
14
|
+
* never reaches another request.
|
|
15
|
+
*/
|
|
16
|
+
export declare function setCspNonce(nonce: string | undefined): void;
|
|
17
|
+
/**
|
|
18
|
+
* Registers a provider's `nonce` as the page's, and returns the function that
|
|
19
|
+
* releases it. For nested providers: `setCspNonce` is then called with the
|
|
20
|
+
* nonce of the deepest claim still held (the latest among equals), the one a
|
|
21
|
+
* component below resolves through context, so an inner provider unmounting
|
|
22
|
+
* leaves its parent's nonce in place, and releasing the last claim puts back
|
|
23
|
+
* the nonce stored before the first one (an app's own `setCspNonce`; one set
|
|
24
|
+
* while a provider is mounted is replaced and not kept). `depth` is the
|
|
25
|
+
* provider's nesting level (0 for the outermost): children mount before their parents, so the order of
|
|
26
|
+
* the calls says nothing about nesting. A no-op on a server (no `document`).
|
|
27
|
+
* Calling the release function twice is harmless.
|
|
28
|
+
*/
|
|
29
|
+
export declare function claimCspNonce(nonce: string | undefined, depth?: number): () => void;
|
|
30
|
+
/**
|
|
31
|
+
* The page's nonce, from the first of:
|
|
32
|
+
*
|
|
33
|
+
* 1. the value given to `setCspNonce`;
|
|
34
|
+
* 2. `<meta property="csp-nonce" nonce="…">`, which Vite's `html.cspNonce`
|
|
35
|
+
* writes, or `<meta name="csp-nonce" content="…">` (its `nonce`, else its
|
|
36
|
+
* `content`);
|
|
37
|
+
* 3. the first `<script nonce>` or `<style nonce>` of the page;
|
|
38
|
+
* 4. `__webpack_nonce__`.
|
|
39
|
+
*
|
|
40
|
+
* `doc` defaults to `globalThis.document`. `undefined` when none has one, and
|
|
41
|
+
* always on a server.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getCspNonce(doc?: Document | undefined): string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* A new `<style>` element of `doc`, not attached, carrying the page's nonce
|
|
46
|
+
* when there is one. Without a global `document`, pass `doc`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createNoncedStyle(doc?: Document): HTMLStyleElement;
|
|
49
|
+
/**
|
|
50
|
+
* `get-nonce`'s API (`getNonce` / `setNonce`), so a bundler can alias that
|
|
51
|
+
* package here and the libraries' bundled dependencies (Radix's scroll lock,
|
|
52
|
+
* through `react-style-singleton`) read the same nonce.
|
|
53
|
+
*/
|
|
54
|
+
export declare const getNonce: () => string | undefined;
|
|
55
|
+
export declare const setNonce: (nonce: string) => void;
|
|
56
|
+
//# sourceMappingURL=csp-nonce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp-nonce.d.ts","sourceRoot":"","sources":["../../src/lib/csp-nonce.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAwBH;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAI3D;AAuBD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,KAAK,SAAI,GACP,MAAM,IAAI,CAYZ;AAwBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAC1B,GAAG,GAAE,QAAQ,GAAG,SAA+B,GAC7C,MAAM,GAAG,SAAS,CAgBpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,GAAG,GAAE,QAA8B,GACjC,gBAAgB,CAKlB;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAAE,MAAM,MAAM,GAAG,SAAuB,CAAC;AAC9D,eAAO,MAAM,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAkB,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/lib/csp-nonce.ts
|
|
2
|
+
var KEY = Symbol.for("nxgt.cspNonce");
|
|
3
|
+
function holder() {
|
|
4
|
+
return globalThis;
|
|
5
|
+
}
|
|
6
|
+
function hasDocument() {
|
|
7
|
+
return typeof document !== "undefined";
|
|
8
|
+
}
|
|
9
|
+
function setCspNonce(nonce) {
|
|
10
|
+
if (!hasDocument())
|
|
11
|
+
return;
|
|
12
|
+
if (nonce)
|
|
13
|
+
holder()[KEY] = nonce;
|
|
14
|
+
else
|
|
15
|
+
delete holder()[KEY];
|
|
16
|
+
}
|
|
17
|
+
var claims = [];
|
|
18
|
+
var unclaimed;
|
|
19
|
+
function winner() {
|
|
20
|
+
let top;
|
|
21
|
+
for (const claim of claims) {
|
|
22
|
+
if (!top || claim.depth >= top.depth)
|
|
23
|
+
top = claim;
|
|
24
|
+
}
|
|
25
|
+
return top;
|
|
26
|
+
}
|
|
27
|
+
function claimCspNonce(nonce, depth = 0) {
|
|
28
|
+
if (!hasDocument())
|
|
29
|
+
return () => {};
|
|
30
|
+
const claim = { nonce, depth };
|
|
31
|
+
if (claims.length === 0)
|
|
32
|
+
unclaimed = holder()[KEY];
|
|
33
|
+
claims.push(claim);
|
|
34
|
+
setCspNonce(winner()?.nonce);
|
|
35
|
+
return () => {
|
|
36
|
+
const index = claims.indexOf(claim);
|
|
37
|
+
if (index < 0)
|
|
38
|
+
return;
|
|
39
|
+
claims.splice(index, 1);
|
|
40
|
+
setCspNonce(claims.length ? winner()?.nonce : unclaimed);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function nonceOf(element) {
|
|
44
|
+
return element.nonce || element.getAttribute("nonce") || element.getAttribute("content") || undefined;
|
|
45
|
+
}
|
|
46
|
+
function firstNonce(doc, selector) {
|
|
47
|
+
for (const element of doc.querySelectorAll(selector)) {
|
|
48
|
+
const nonce = nonceOf(element);
|
|
49
|
+
if (nonce)
|
|
50
|
+
return nonce;
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
function getCspNonce(doc = globalThis.document) {
|
|
55
|
+
if (!doc)
|
|
56
|
+
return;
|
|
57
|
+
const stored = holder()[KEY];
|
|
58
|
+
if (stored)
|
|
59
|
+
return stored;
|
|
60
|
+
const meta = firstNonce(doc, 'meta[property="csp-nonce"], meta[name="csp-nonce"]');
|
|
61
|
+
if (meta)
|
|
62
|
+
return meta;
|
|
63
|
+
const tag = firstNonce(doc, "script[nonce], style[nonce]");
|
|
64
|
+
if (tag)
|
|
65
|
+
return tag;
|
|
66
|
+
const webpack = typeof __webpack_nonce__ !== "undefined" ? __webpack_nonce__ : holder().__webpack_nonce__;
|
|
67
|
+
return typeof webpack === "string" && webpack ? webpack : undefined;
|
|
68
|
+
}
|
|
69
|
+
function createNoncedStyle(doc = globalThis.document) {
|
|
70
|
+
const style = doc.createElement("style");
|
|
71
|
+
const nonce = getCspNonce(doc);
|
|
72
|
+
if (nonce)
|
|
73
|
+
style.setAttribute("nonce", nonce);
|
|
74
|
+
return style;
|
|
75
|
+
}
|
|
76
|
+
var getNonce = getCspNonce;
|
|
77
|
+
var setNonce = setCspNonce;
|
|
78
|
+
export {
|
|
79
|
+
claimCspNonce,
|
|
80
|
+
createNoncedStyle,
|
|
81
|
+
getCspNonce,
|
|
82
|
+
getNonce,
|
|
83
|
+
setCspNonce,
|
|
84
|
+
setNonce
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
//# debugId=79F62D9AAEDF578C64756E2164756E21
|
|
88
|
+
//# sourceMappingURL=csp-nonce.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/csp-nonce.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * The page's Content Security Policy nonce, for the code that creates a\n * `<style>` (or `<script>`) element at runtime: a strict `style-src-elem\n * 'nonce-X'` blocks any element that does not carry it.\n *\n * Browser only. On a server the nonce belongs to one request, so it is never\n * kept in a module global there: `setCspNonce` does nothing without a\n * `document`, and `getCspNonce` then answers `undefined`. Pass the nonce down\n * the request instead (an argument, a context, `AsyncLocalStorage`).\n */\n\n/**\n * Where the value lives: a registered symbol on `globalThis`, so two copies of\n * this package in one bundle (a library's and the app's) share it.\n */\nconst KEY = Symbol.for('nxgt.cspNonce');\n\n/** webpack rewrites this free identifier; read it before the global. */\ndeclare const __webpack_nonce__: unknown;\n\ntype NonceHolder = { [KEY]?: string; __webpack_nonce__?: unknown };\n\n/** An element whose `nonce` the browser may have hidden from its attribute. */\ntype NoncedElement = Element & { nonce?: string };\n\nfunction holder(): NonceHolder {\n\treturn globalThis as unknown as NonceHolder;\n}\n\nfunction hasDocument(): boolean {\n\treturn typeof document !== 'undefined';\n}\n\n/**\n * Keeps `nonce` for the page, read first by `getCspNonce`. `undefined` (or\n * `''`) forgets it. A no-op on a server (no `document`), so a request's nonce\n * never reaches another request.\n */\nexport function setCspNonce(nonce: string | undefined): void {\n\tif (!hasDocument()) return;\n\tif (nonce) holder()[KEY] = nonce;\n\telse delete holder()[KEY];\n}\n\n/** A provider's claim on the stored nonce. */\ntype Claim = { nonce: string | undefined; depth: number };\n\n/** The claims of the mounted providers, in mounting order. */\nconst claims: Claim[] = [];\n\n/**\n * What `setCspNonce` had stored before the first claim, put back once the\n * last one is released.\n */\nlet unclaimed: string | undefined;\n\n/** The deepest claim, the latest one among equals. */\nfunction winner(): Claim | undefined {\n\tlet top: Claim | undefined;\n\tfor (const claim of claims) {\n\t\tif (!top || claim.depth >= top.depth) top = claim;\n\t}\n\treturn top;\n}\n\n/**\n * Registers a provider's `nonce` as the page's, and returns the function that\n * releases it. For nested providers: `setCspNonce` is then called with the\n * nonce of the deepest claim still held (the latest among equals), the one a\n * component below resolves through context, so an inner provider unmounting\n * leaves its parent's nonce in place, and releasing the last claim puts back\n * the nonce stored before the first one (an app's own `setCspNonce`; one set\n * while a provider is mounted is replaced and not kept). `depth` is the\n * provider's nesting level (0 for the outermost): children mount before their parents, so the order of\n * the calls says nothing about nesting. A no-op on a server (no `document`).\n * Calling the release function twice is harmless.\n */\nexport function claimCspNonce(\n\tnonce: string | undefined,\n\tdepth = 0,\n): () => void {\n\tif (!hasDocument()) return () => {};\n\tconst claim: Claim = { nonce, depth };\n\tif (claims.length === 0) unclaimed = holder()[KEY];\n\tclaims.push(claim);\n\tsetCspNonce(winner()?.nonce);\n\treturn () => {\n\t\tconst index = claims.indexOf(claim);\n\t\tif (index < 0) return;\n\t\tclaims.splice(index, 1);\n\t\tsetCspNonce(claims.length ? winner()?.nonce : unclaimed);\n\t};\n}\n\n/**\n * Browsers hide a `nonce` attribute's value once the page is parsed (it reads\n * `''`), and keep it on the `nonce` property: read that first.\n */\nfunction nonceOf(element: NoncedElement): string | undefined {\n\treturn (\n\t\telement.nonce ||\n\t\telement.getAttribute('nonce') ||\n\t\telement.getAttribute('content') ||\n\t\tundefined\n\t);\n}\n\n/** The first non-empty nonce among the elements `selector` matches. */\nfunction firstNonce(doc: Document, selector: string): string | undefined {\n\tfor (const element of doc.querySelectorAll(selector)) {\n\t\tconst nonce = nonceOf(element);\n\t\tif (nonce) return nonce;\n\t}\n\treturn undefined;\n}\n\n/**\n * The page's nonce, from the first of:\n *\n * 1. the value given to `setCspNonce`;\n * 2. `<meta property=\"csp-nonce\" nonce=\"…\">`, which Vite's `html.cspNonce`\n * writes, or `<meta name=\"csp-nonce\" content=\"…\">` (its `nonce`, else its\n * `content`);\n * 3. the first `<script nonce>` or `<style nonce>` of the page;\n * 4. `__webpack_nonce__`.\n *\n * `doc` defaults to `globalThis.document`. `undefined` when none has one, and\n * always on a server.\n */\nexport function getCspNonce(\n\tdoc: Document | undefined = globalThis.document,\n): string | undefined {\n\tif (!doc) return undefined;\n\tconst stored = holder()[KEY];\n\tif (stored) return stored;\n\tconst meta = firstNonce(\n\t\tdoc,\n\t\t'meta[property=\"csp-nonce\"], meta[name=\"csp-nonce\"]',\n\t);\n\tif (meta) return meta;\n\tconst tag = firstNonce(doc, 'script[nonce], style[nonce]');\n\tif (tag) return tag;\n\tconst webpack =\n\t\ttypeof __webpack_nonce__ !== 'undefined'\n\t\t\t? __webpack_nonce__\n\t\t\t: holder().__webpack_nonce__;\n\treturn typeof webpack === 'string' && webpack ? webpack : undefined;\n}\n\n/**\n * A new `<style>` element of `doc`, not attached, carrying the page's nonce\n * when there is one. Without a global `document`, pass `doc`.\n */\nexport function createNoncedStyle(\n\tdoc: Document = globalThis.document,\n): HTMLStyleElement {\n\tconst style = doc.createElement('style');\n\tconst nonce = getCspNonce(doc);\n\tif (nonce) style.setAttribute('nonce', nonce);\n\treturn style;\n}\n\n/**\n * `get-nonce`'s API (`getNonce` / `setNonce`), so a bundler can alias that\n * package here and the libraries' bundled dependencies (Radix's scroll lock,\n * through `react-style-singleton`) read the same nonce.\n */\nexport const getNonce: () => string | undefined = getCspNonce;\nexport const setNonce: (nonce: string) => void = setCspNonce;\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAeA,IAAM,MAAM,OAAO,IAAI,eAAe;AAUtC,SAAS,MAAM,GAAgB;AAAA,EAC9B,OAAO;AAAA;AAGR,SAAS,WAAW,GAAY;AAAA,EAC/B,OAAO,OAAO,aAAa;AAAA;AAQrB,SAAS,WAAW,CAAC,OAAiC;AAAA,EAC5D,IAAI,CAAC,YAAY;AAAA,IAAG;AAAA,EACpB,IAAI;AAAA,IAAO,OAAO,EAAE,OAAO;AAAA,EACtB;AAAA,WAAO,OAAO,EAAE;AAAA;AAOtB,IAAM,SAAkB,CAAC;AAMzB,IAAI;AAGJ,SAAS,MAAM,GAAsB;AAAA,EACpC,IAAI;AAAA,EACJ,WAAW,SAAS,QAAQ;AAAA,IAC3B,IAAI,CAAC,OAAO,MAAM,SAAS,IAAI;AAAA,MAAO,MAAM;AAAA,EAC7C;AAAA,EACA,OAAO;AAAA;AAeD,SAAS,aAAa,CAC5B,OACA,QAAQ,GACK;AAAA,EACb,IAAI,CAAC,YAAY;AAAA,IAAG,OAAO,MAAM;AAAA,EACjC,MAAM,QAAe,EAAE,OAAO,MAAM;AAAA,EACpC,IAAI,OAAO,WAAW;AAAA,IAAG,YAAY,OAAO,EAAE;AAAA,EAC9C,OAAO,KAAK,KAAK;AAAA,EACjB,YAAY,OAAO,GAAG,KAAK;AAAA,EAC3B,OAAO,MAAM;AAAA,IACZ,MAAM,QAAQ,OAAO,QAAQ,KAAK;AAAA,IAClC,IAAI,QAAQ;AAAA,MAAG;AAAA,IACf,OAAO,OAAO,OAAO,CAAC;AAAA,IACtB,YAAY,OAAO,SAAS,OAAO,GAAG,QAAQ,SAAS;AAAA;AAAA;AAQzD,SAAS,OAAO,CAAC,SAA4C;AAAA,EAC5D,OACC,QAAQ,SACR,QAAQ,aAAa,OAAO,KAC5B,QAAQ,aAAa,SAAS,KAC9B;AAAA;AAKF,SAAS,UAAU,CAAC,KAAe,UAAsC;AAAA,EACxE,WAAW,WAAW,IAAI,iBAAiB,QAAQ,GAAG;AAAA,IACrD,MAAM,QAAQ,QAAQ,OAAO;AAAA,IAC7B,IAAI;AAAA,MAAO,OAAO;AAAA,EACnB;AAAA,EACA;AAAA;AAgBM,SAAS,WAAW,CAC1B,MAA4B,WAAW,UAClB;AAAA,EACrB,IAAI,CAAC;AAAA,IAAK;AAAA,EACV,MAAM,SAAS,OAAO,EAAE;AAAA,EACxB,IAAI;AAAA,IAAQ,OAAO;AAAA,EACnB,MAAM,OAAO,WACZ,KACA,oDACD;AAAA,EACA,IAAI;AAAA,IAAM,OAAO;AAAA,EACjB,MAAM,MAAM,WAAW,KAAK,6BAA6B;AAAA,EACzD,IAAI;AAAA,IAAK,OAAO;AAAA,EAChB,MAAM,UACL,OAAO,sBAAsB,cAC1B,oBACA,OAAO,EAAE;AAAA,EACb,OAAO,OAAO,YAAY,YAAY,UAAU,UAAU;AAAA;AAOpD,SAAS,iBAAiB,CAChC,MAAgB,WAAW,UACR;AAAA,EACnB,MAAM,QAAQ,IAAI,cAAc,OAAO;AAAA,EACvC,MAAM,QAAQ,YAAY,GAAG;AAAA,EAC7B,IAAI;AAAA,IAAO,MAAM,aAAa,SAAS,KAAK;AAAA,EAC5C,OAAO;AAAA;AAQD,IAAM,WAAqC;AAC3C,IAAM,WAAoC;",
|
|
8
|
+
"debugId": "79F62D9AAEDF578C64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type Debounced<Args extends unknown[]> = ((...args: Args) => void) & {
|
|
2
|
+
cancel: () => void;
|
|
3
|
+
flush: () => void;
|
|
4
|
+
};
|
|
5
|
+
export declare function debounce<Args extends unknown[]>(fn: (...args: Args) => void, ms: number): Debounced<Args>;
|
|
6
|
+
//# sourceMappingURL=debounce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.d.ts","sourceRoot":"","sources":["../../src/lib/debounce.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,CAAC,IAAI,SAAS,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC,GAAG;IAC3E,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,IAAI,SAAS,OAAO,EAAE,EAC9C,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,EAC3B,EAAE,EAAE,MAAM,GACR,SAAS,CAAC,IAAI,CAAC,CAgCjB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An object built the first time it is used, not when its module loads: for a
|
|
3
|
+
* constant whose construction has a cost or a side effect the importer must be
|
|
4
|
+
* able to configure first. The libraries' deprecated zod schema constants are
|
|
5
|
+
* the reason: building a `z.object()` makes zod probe `new Function`, which a
|
|
6
|
+
* strict Content Security Policy reports, and an app can only switch the probe
|
|
7
|
+
* off (`z.config({ jitless: true })`) before the first schema is built.
|
|
8
|
+
*
|
|
9
|
+
* The stand-in is a `Proxy`. Its first trap of any kind builds the object, then
|
|
10
|
+
* copies the object's own properties and prototype onto the proxy's target, so
|
|
11
|
+
* every later operation sees what the built object holds: property reads and
|
|
12
|
+
* method calls, `in`, `Object.keys`, a spread, `instanceof` (through the
|
|
13
|
+
* prototype, or a `Symbol.hasInstance` that reads the object's properties),
|
|
14
|
+
* and the invariants a `Proxy` must keep for non-configurable properties.
|
|
15
|
+
*
|
|
16
|
+
* The stand-in is not the built object itself (`===` tells them apart), and a
|
|
17
|
+
* property written on it lands on the copy. Use it for a value its callers
|
|
18
|
+
* read, never for one they compare by identity, or register: a `.meta()` or
|
|
19
|
+
* `registry.add()` on the stand-in is not seen through the factory's schema.
|
|
20
|
+
*
|
|
21
|
+
* A read of the stand-in while `build` runs throws; if `build` throws, the
|
|
22
|
+
* stand-in goes back to unbuilt and the next use builds again.
|
|
23
|
+
*/
|
|
24
|
+
/** `build`'s object, built on the first operation on the returned stand-in. */
|
|
25
|
+
export declare function lazyObject<T extends object>(build: () => T): T;
|
|
26
|
+
/**
|
|
27
|
+
* A function that builds `build`'s value on its first call and returns that
|
|
28
|
+
* same value on every call after: a schema factory.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildOnce<T>(build: () => T): () => T;
|
|
31
|
+
//# sourceMappingURL=lazy-object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-object.d.ts","sourceRoot":"","sources":["../../src/lib/lazy-object.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAuC9D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAgBpD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// src/lib/lazy-object.ts
|
|
2
|
+
function lazyObject(build) {
|
|
3
|
+
const target = {};
|
|
4
|
+
let state = "idle";
|
|
5
|
+
const ensure = () => {
|
|
6
|
+
if (state === "building") {
|
|
7
|
+
throw new Error("lazyObject: the object was read while it was building");
|
|
8
|
+
}
|
|
9
|
+
if (state === "idle") {
|
|
10
|
+
state = "building";
|
|
11
|
+
try {
|
|
12
|
+
const value = build();
|
|
13
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(value));
|
|
14
|
+
Object.setPrototypeOf(target, Object.getPrototypeOf(value));
|
|
15
|
+
state = "built";
|
|
16
|
+
} catch (error) {
|
|
17
|
+
state = "idle";
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return target;
|
|
22
|
+
};
|
|
23
|
+
return new Proxy(target, {
|
|
24
|
+
get: (_, key) => Reflect.get(ensure(), key),
|
|
25
|
+
set: (_, key, value) => Reflect.set(ensure(), key, value),
|
|
26
|
+
has: (_, key) => Reflect.has(ensure(), key),
|
|
27
|
+
ownKeys: () => Reflect.ownKeys(ensure()),
|
|
28
|
+
getOwnPropertyDescriptor: (_, key) => Reflect.getOwnPropertyDescriptor(ensure(), key),
|
|
29
|
+
defineProperty: (_, key, descriptor) => Reflect.defineProperty(ensure(), key, descriptor),
|
|
30
|
+
deleteProperty: (_, key) => Reflect.deleteProperty(ensure(), key),
|
|
31
|
+
getPrototypeOf: () => Reflect.getPrototypeOf(ensure()),
|
|
32
|
+
setPrototypeOf: (_, proto) => Reflect.setPrototypeOf(ensure(), proto),
|
|
33
|
+
isExtensible: () => Reflect.isExtensible(ensure()),
|
|
34
|
+
preventExtensions: () => Reflect.preventExtensions(ensure())
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function buildOnce(build) {
|
|
38
|
+
let value;
|
|
39
|
+
let building = false;
|
|
40
|
+
return () => {
|
|
41
|
+
if (value)
|
|
42
|
+
return value.current;
|
|
43
|
+
if (building) {
|
|
44
|
+
throw new Error("buildOnce: the value was read while it was building");
|
|
45
|
+
}
|
|
46
|
+
building = true;
|
|
47
|
+
try {
|
|
48
|
+
value = { current: build() };
|
|
49
|
+
} finally {
|
|
50
|
+
building = false;
|
|
51
|
+
}
|
|
52
|
+
return value.current;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
buildOnce,
|
|
57
|
+
lazyObject
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
//# debugId=FF185767F4125A5364756E2164756E21
|
|
61
|
+
//# sourceMappingURL=lazy-object.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/lazy-object.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * An object built the first time it is used, not when its module loads: for a\n * constant whose construction has a cost or a side effect the importer must be\n * able to configure first. The libraries' deprecated zod schema constants are\n * the reason: building a `z.object()` makes zod probe `new Function`, which a\n * strict Content Security Policy reports, and an app can only switch the probe\n * off (`z.config({ jitless: true })`) before the first schema is built.\n *\n * The stand-in is a `Proxy`. Its first trap of any kind builds the object, then\n * copies the object's own properties and prototype onto the proxy's target, so\n * every later operation sees what the built object holds: property reads and\n * method calls, `in`, `Object.keys`, a spread, `instanceof` (through the\n * prototype, or a `Symbol.hasInstance` that reads the object's properties),\n * and the invariants a `Proxy` must keep for non-configurable properties.\n *\n * The stand-in is not the built object itself (`===` tells them apart), and a\n * property written on it lands on the copy. Use it for a value its callers\n * read, never for one they compare by identity, or register: a `.meta()` or\n * `registry.add()` on the stand-in is not seen through the factory's schema.\n *\n * A read of the stand-in while `build` runs throws; if `build` throws, the\n * stand-in goes back to unbuilt and the next use builds again.\n */\n\n/** `build`'s object, built on the first operation on the returned stand-in. */\nexport function lazyObject<T extends object>(build: () => T): T {\n\tconst target = {} as T;\n\tlet state: 'idle' | 'building' | 'built' = 'idle';\n\tconst ensure = (): T => {\n\t\tif (state === 'building') {\n\t\t\tthrow new Error('lazyObject: the object was read while it was building');\n\t\t}\n\t\tif (state === 'idle') {\n\t\t\tstate = 'building';\n\t\t\ttry {\n\t\t\t\tconst value = build();\n\t\t\t\tObject.defineProperties(\n\t\t\t\t\ttarget,\n\t\t\t\t\tObject.getOwnPropertyDescriptors(value),\n\t\t\t\t);\n\t\t\t\tObject.setPrototypeOf(target, Object.getPrototypeOf(value));\n\t\t\t\tstate = 'built';\n\t\t\t} catch (error) {\n\t\t\t\tstate = 'idle';\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn target;\n\t};\n\treturn new Proxy(target, {\n\t\tget: (_, key) => Reflect.get(ensure(), key),\n\t\tset: (_, key, value) => Reflect.set(ensure(), key, value),\n\t\thas: (_, key) => Reflect.has(ensure(), key),\n\t\townKeys: () => Reflect.ownKeys(ensure()),\n\t\tgetOwnPropertyDescriptor: (_, key) =>\n\t\t\tReflect.getOwnPropertyDescriptor(ensure(), key),\n\t\tdefineProperty: (_, key, descriptor) =>\n\t\t\tReflect.defineProperty(ensure(), key, descriptor),\n\t\tdeleteProperty: (_, key) => Reflect.deleteProperty(ensure(), key),\n\t\tgetPrototypeOf: () => Reflect.getPrototypeOf(ensure()),\n\t\tsetPrototypeOf: (_, proto) => Reflect.setPrototypeOf(ensure(), proto),\n\t\tisExtensible: () => Reflect.isExtensible(ensure()),\n\t\tpreventExtensions: () => Reflect.preventExtensions(ensure()),\n\t});\n}\n\n/**\n * A function that builds `build`'s value on its first call and returns that\n * same value on every call after: a schema factory.\n */\nexport function buildOnce<T>(build: () => T): () => T {\n\tlet value: { current: T } | undefined;\n\tlet building = false;\n\treturn () => {\n\t\tif (value) return value.current;\n\t\tif (building) {\n\t\t\tthrow new Error('buildOnce: the value was read while it was building');\n\t\t}\n\t\tbuilding = true;\n\t\ttry {\n\t\t\tvalue = { current: build() };\n\t\t} finally {\n\t\t\tbuilding = false;\n\t\t}\n\t\treturn value.current;\n\t};\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAyBO,SAAS,UAA4B,CAAC,OAAmB;AAAA,EAC/D,MAAM,SAAS,CAAC;AAAA,EAChB,IAAI,QAAuC;AAAA,EAC3C,MAAM,SAAS,MAAS;AAAA,IACvB,IAAI,UAAU,YAAY;AAAA,MACzB,MAAM,IAAI,MAAM,uDAAuD;AAAA,IACxE;AAAA,IACA,IAAI,UAAU,QAAQ;AAAA,MACrB,QAAQ;AAAA,MACR,IAAI;AAAA,QACH,MAAM,QAAQ,MAAM;AAAA,QACpB,OAAO,iBACN,QACA,OAAO,0BAA0B,KAAK,CACvC;AAAA,QACA,OAAO,eAAe,QAAQ,OAAO,eAAe,KAAK,CAAC;AAAA,QAC1D,QAAQ;AAAA,QACP,OAAO,OAAO;AAAA,QACf,QAAQ;AAAA,QACR,MAAM;AAAA;AAAA,IAER;AAAA,IACA,OAAO;AAAA;AAAA,EAER,OAAO,IAAI,MAAM,QAAQ;AAAA,IACxB,KAAK,CAAC,GAAG,QAAQ,QAAQ,IAAI,OAAO,GAAG,GAAG;AAAA,IAC1C,KAAK,CAAC,GAAG,KAAK,UAAU,QAAQ,IAAI,OAAO,GAAG,KAAK,KAAK;AAAA,IACxD,KAAK,CAAC,GAAG,QAAQ,QAAQ,IAAI,OAAO,GAAG,GAAG;AAAA,IAC1C,SAAS,MAAM,QAAQ,QAAQ,OAAO,CAAC;AAAA,IACvC,0BAA0B,CAAC,GAAG,QAC7B,QAAQ,yBAAyB,OAAO,GAAG,GAAG;AAAA,IAC/C,gBAAgB,CAAC,GAAG,KAAK,eACxB,QAAQ,eAAe,OAAO,GAAG,KAAK,UAAU;AAAA,IACjD,gBAAgB,CAAC,GAAG,QAAQ,QAAQ,eAAe,OAAO,GAAG,GAAG;AAAA,IAChE,gBAAgB,MAAM,QAAQ,eAAe,OAAO,CAAC;AAAA,IACrD,gBAAgB,CAAC,GAAG,UAAU,QAAQ,eAAe,OAAO,GAAG,KAAK;AAAA,IACpE,cAAc,MAAM,QAAQ,aAAa,OAAO,CAAC;AAAA,IACjD,mBAAmB,MAAM,QAAQ,kBAAkB,OAAO,CAAC;AAAA,EAC5D,CAAC;AAAA;AAOK,SAAS,SAAY,CAAC,OAAyB;AAAA,EACrD,IAAI;AAAA,EACJ,IAAI,WAAW;AAAA,EACf,OAAO,MAAM;AAAA,IACZ,IAAI;AAAA,MAAO,OAAO,MAAM;AAAA,IACxB,IAAI,UAAU;AAAA,MACb,MAAM,IAAI,MAAM,qDAAqD;AAAA,IACtE;AAAA,IACA,WAAW;AAAA,IACX,IAAI;AAAA,MACH,QAAQ,EAAE,SAAS,MAAM,EAAE;AAAA,cAC1B;AAAA,MACD,WAAW;AAAA;AAAA,IAEZ,OAAO,MAAM;AAAA;AAAA;",
|
|
8
|
+
"debugId": "FF185767F4125A5364756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** What a combobox's polite live region says about its list, if anything. */
|
|
2
|
+
export type SearchAnnouncement = {
|
|
3
|
+
kind: 'loading';
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'error';
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'results';
|
|
8
|
+
count: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The announcement for a list: its failure, else that it is loading, else
|
|
12
|
+
* its count once the query has `settled` (searched, or typing paused) —
|
|
13
|
+
* `null` while the user is still typing, so a screen reader is not told
|
|
14
|
+
* about every keystroke.
|
|
15
|
+
*/
|
|
16
|
+
export declare function searchAnnouncement(list: {
|
|
17
|
+
loading: boolean;
|
|
18
|
+
failed: boolean;
|
|
19
|
+
settled: boolean;
|
|
20
|
+
count: number;
|
|
21
|
+
}): SearchAnnouncement | null;
|
|
22
|
+
//# sourceMappingURL=search-announcement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-announcement.d.ts","sourceRoot":"","sources":["../../src/lib/search-announcement.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,MAAM,kBAAkB,GAC3B;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd,GAAG,kBAAkB,GAAG,IAAI,CAI5B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accent- and case-insensitive text matching, for a list filtered as the user
|
|
3
|
+
* types: `'e'` finds "École", `'ecole'` finds "ÉCOLE".
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* `text` lowercased for `locale` (the runtime's when omitted), then without
|
|
7
|
+
* its diacritics: `foldText('Élève')` is `'eleve'`. Lowercasing comes first,
|
|
8
|
+
* so a Turkish `'İ'` (`'tr'`) folds to `'i'` rather than keeping a dot.
|
|
9
|
+
*/
|
|
10
|
+
export declare function foldText(text: string, locale?: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Whether `query` occurs in `text`, ignoring case and accents. A query of
|
|
13
|
+
* spaces only matches everything, as an empty one does.
|
|
14
|
+
*/
|
|
15
|
+
export declare function matchesText(text: string, query: string, locale?: string): boolean;
|
|
16
|
+
//# sourceMappingURL=text-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-match.d.ts","sourceRoot":"","sources":["../../src/lib/text-match.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAWD;;;GAGG;AACH,wBAAgB,WAAW,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAIT"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/lib/text-match.ts
|
|
2
|
+
var DIACRITICS = /[̀-ͯ]/g;
|
|
3
|
+
function foldText(text, locale) {
|
|
4
|
+
return lowercase(text, locale).normalize("NFD").replace(DIACRITICS, "");
|
|
5
|
+
}
|
|
6
|
+
function lowercase(text, locale) {
|
|
7
|
+
try {
|
|
8
|
+
return text.toLocaleLowerCase(locale);
|
|
9
|
+
} catch {
|
|
10
|
+
return text.toLocaleLowerCase();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function matchesText(text, query, locale) {
|
|
14
|
+
const needle = foldText(query.trim(), locale);
|
|
15
|
+
if (!needle)
|
|
16
|
+
return true;
|
|
17
|
+
return foldText(text, locale).includes(needle);
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
foldText,
|
|
21
|
+
matchesText
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
//# debugId=FCBE43FC1537D3D864756E2164756E21
|
|
25
|
+
//# sourceMappingURL=text-match.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/text-match.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Accent- and case-insensitive text matching, for a list filtered as the user\n * types: `'e'` finds \"École\", `'ecole'` finds \"ÉCOLE\".\n */\n\n/** Combining diacritical marks, which `normalize('NFD')` splits from their letter. */\nconst DIACRITICS = /[̀-ͯ]/g;\n\n/**\n * `text` lowercased for `locale` (the runtime's when omitted), then without\n * its diacritics: `foldText('Élève')` is `'eleve'`. Lowercasing comes first,\n * so a Turkish `'İ'` (`'tr'`) folds to `'i'` rather than keeping a dot.\n */\nexport function foldText(text: string, locale?: string): string {\n\treturn lowercase(text, locale).normalize('NFD').replace(DIACRITICS, '');\n}\n\nfunction lowercase(text: string, locale?: string): string {\n\ttry {\n\t\treturn text.toLocaleLowerCase(locale);\n\t} catch {\n\t\t// An invalid language tag: the runtime's own rules, never a throw.\n\t\treturn text.toLocaleLowerCase();\n\t}\n}\n\n/**\n * Whether `query` occurs in `text`, ignoring case and accents. A query of\n * spaces only matches everything, as an empty one does.\n */\nexport function matchesText(\n\ttext: string,\n\tquery: string,\n\tlocale?: string,\n): boolean {\n\tconst needle = foldText(query.trim(), locale);\n\tif (!needle) return true;\n\treturn foldText(text, locale).includes(needle);\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAMA,IAAM,aAAa;AAOZ,SAAS,QAAQ,CAAC,MAAc,QAAyB;AAAA,EAC/D,OAAO,UAAU,MAAM,MAAM,EAAE,UAAU,KAAK,EAAE,QAAQ,YAAY,EAAE;AAAA;AAGvE,SAAS,SAAS,CAAC,MAAc,QAAyB;AAAA,EACzD,IAAI;AAAA,IACH,OAAO,KAAK,kBAAkB,MAAM;AAAA,IACnC,MAAM;AAAA,IAEP,OAAO,KAAK,kBAAkB;AAAA;AAAA;AAQzB,SAAS,WAAW,CAC1B,MACA,OACA,QACU;AAAA,EACV,MAAM,SAAS,SAAS,MAAM,KAAK,GAAG,MAAM;AAAA,EAC5C,IAAI,CAAC;AAAA,IAAQ,OAAO;AAAA,EACpB,OAAO,SAAS,MAAM,MAAM,EAAE,SAAS,MAAM;AAAA;",
|
|
8
|
+
"debugId": "FCBE43FC1537D3D864756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite's `build.assetsInlineLimit` for a page under a strict Content Security
|
|
3
|
+
* Policy: a font is **never** inlined, every other asset keeps Vite's default
|
|
4
|
+
* (inlined under 4096 bytes).
|
|
5
|
+
*
|
|
6
|
+
* Vite turns a small asset into a `data:` URI, and `font-src 'self'` blocks a
|
|
7
|
+
* font served that way (KaTeX's `KaTeX_Size3-Regular.woff2` is 3.6 KB). Images
|
|
8
|
+
* are usually fine as `data:` URIs (`img-src 'self' data:`), so only fonts are
|
|
9
|
+
* kept as files. Returning `undefined` hands the decision back to Vite.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // vite.config.ts
|
|
13
|
+
* import { cspAssetsInlineLimit } from '@nxgt/material-core/vite';
|
|
14
|
+
* import { defineConfig } from 'vite';
|
|
15
|
+
*
|
|
16
|
+
* export default defineConfig({ build: { assetsInlineLimit: cspAssetsInlineLimit } });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* To keep more out (an SVG, say), wrap it:
|
|
20
|
+
* `(file, content) => file.endsWith('.svg') ? false : cspAssetsInlineLimit(file, content)`.
|
|
21
|
+
*
|
|
22
|
+
* `content` is typed as a `Uint8Array` (Vite passes a `Buffer`, which is one),
|
|
23
|
+
* so the declaration needs no Node types; it is not read.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cspAssetsInlineLimit(file: string, _content?: Uint8Array): boolean | undefined;
|
|
26
|
+
//# sourceMappingURL=csp-assets-inline-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp-assets-inline-limit.d.ts","sourceRoot":"","sources":["../../src/vite/csp-assets-inline-limit.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,UAAU,GACnB,OAAO,GAAG,SAAS,CAErB"}
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
* not have to rediscover it — and because there is exactly one copy of the fix
|
|
9
9
|
* to maintain, shared by `@nxgt/material` and `@nxgt/material-vue`.
|
|
10
10
|
*
|
|
11
|
+
* It also holds `cspAssetsInlineLimit`, the `build.assetsInlineLimit` that
|
|
12
|
+
* keeps fonts out of `data:` URIs for a strict Content Security Policy.
|
|
13
|
+
*
|
|
11
14
|
* It imports nothing at runtime: `Plugin` is a type, erased by the compiler, so
|
|
12
15
|
* the built `dist/vite.js` has no dependency on `vite` at all.
|
|
13
16
|
*/
|
|
14
17
|
import type { Plugin } from 'vite';
|
|
18
|
+
export { cspAssetsInlineLimit } from './csp-assets-inline-limit';
|
|
15
19
|
/**
|
|
16
20
|
* Bundles pdfkit's fourteen standard fonts instead of leaving them to a
|
|
17
21
|
* `require` that cannot work in a bundle.
|
package/dist/vite/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CA6C5C"}
|
package/dist/vite/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// src/vite/csp-assets-inline-limit.ts
|
|
2
|
+
var FONT = /\.(woff2?|ttf|otf|eot)(\?.*)?$/i;
|
|
3
|
+
function cspAssetsInlineLimit(file, _content) {
|
|
4
|
+
return FONT.test(file) ? false : undefined;
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
// src/vite/index.ts
|
|
2
8
|
function pdfkitStandardFonts() {
|
|
3
9
|
const CALL = /[A-Za-z_$][\w$]*\(\s*(['"])#standard-fonts\/(\w+)\1\s*\)/g;
|
|
@@ -24,8 +30,9 @@ ${out}`, map: null };
|
|
|
24
30
|
};
|
|
25
31
|
}
|
|
26
32
|
export {
|
|
33
|
+
cspAssetsInlineLimit,
|
|
27
34
|
pdfkitStandardFonts
|
|
28
35
|
};
|
|
29
36
|
|
|
30
|
-
//# debugId=
|
|
37
|
+
//# debugId=C84B59B7E2D9781E64756E2164756E21
|
|
31
38
|
//# sourceMappingURL=index.js.map
|
package/dist/vite/index.js.map
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/vite/index.ts"],
|
|
3
|
+
"sources": ["../src/vite/csp-assets-inline-limit.ts", "../src/vite/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"
|
|
5
|
+
"/** A font file, by extension, with or without a query string. */\nconst FONT = /\\.(woff2?|ttf|otf|eot)(\\?.*)?$/i;\n\n/**\n * Vite's `build.assetsInlineLimit` for a page under a strict Content Security\n * Policy: a font is **never** inlined, every other asset keeps Vite's default\n * (inlined under 4096 bytes).\n *\n * Vite turns a small asset into a `data:` URI, and `font-src 'self'` blocks a\n * font served that way (KaTeX's `KaTeX_Size3-Regular.woff2` is 3.6 KB). Images\n * are usually fine as `data:` URIs (`img-src 'self' data:`), so only fonts are\n * kept as files. Returning `undefined` hands the decision back to Vite.\n *\n * ```ts\n * // vite.config.ts\n * import { cspAssetsInlineLimit } from '@nxgt/material-core/vite';\n * import { defineConfig } from 'vite';\n *\n * export default defineConfig({ build: { assetsInlineLimit: cspAssetsInlineLimit } });\n * ```\n *\n * To keep more out (an SVG, say), wrap it:\n * `(file, content) => file.endsWith('.svg') ? false : cspAssetsInlineLimit(file, content)`.\n *\n * `content` is typed as a `Uint8Array` (Vite passes a `Buffer`, which is one),\n * so the declaration needs no Node types; it is not read.\n */\nexport function cspAssetsInlineLimit(\n\tfile: string,\n\t_content?: Uint8Array,\n): boolean | undefined {\n\treturn FONT.test(file) ? false : undefined;\n}\n",
|
|
6
|
+
"/**\n * Build-time helpers for the apps that consume this library.\n *\n * This entry is the odd one out: everything else here runs in a browser or in\n * an SSR server, and the `/vite` entry is Node code that runs inside a\n * consumer's `vite.config.ts` (or a Nuxt app's Nitro build). It is here anyway\n * because the problem it solves is this package's to own — a consumer should\n * not have to rediscover it — and because there is exactly one copy of the fix\n * to maintain, shared by `@nxgt/material` and `@nxgt/material-vue`.\n *\n * It also holds `cspAssetsInlineLimit`, the `build.assetsInlineLimit` that\n * keeps fonts out of `data:` URIs for a strict Content Security Policy.\n *\n * It imports nothing at runtime: `Plugin` is a type, erased by the compiler, so\n * the built `dist/vite.js` has no dependency on `vite` at all.\n */\n\nimport type { Plugin } from 'vite';\n\nexport { cspAssetsInlineLimit } from './csp-assets-inline-limit';\n\n/**\n * Bundles pdfkit's fourteen standard fonts instead of leaving them to a\n * `require` that cannot work in a bundle.\n *\n * **Only needed by an app that renders PDFs** (`@nxgt/material/print`, or\n * `@nxgt/material-vue/pdf`). No other entry reaches `@react-pdf/renderer`, so\n * an app that does not print never sees this at all.\n * If you do print, read on, because the failure is spectacular and silent until\n * production.\n *\n * Both are built on `@react-pdf/renderer`, which is built on\n * `pdfkit`, whose node build ends with:\n *\n * ```js\n * const require$1 = createRequire(import.meta.url);\n * registerStdFontLoaders({ Helvetica: () => require$1('#standard-fonts/Helvetica'), … });\n * ```\n *\n * `#standard-fonts/*` is a package-`imports` entry **private to pdfkit**, and\n * that is a RUNTIME call, not an import — so no bundler resolves it. Bundle\n * pdfkit into an SSR server (`ssr.noExternal`) and the literal survives into the\n * output, where `createRequire` is rooted at the build directory and the importer\n * is no longer inside pdfkit. The server then throws on **every request**,\n * `/health` included:\n *\n * ```\n * error: Cannot find module '#standard-fonts/Helvetica' from '…/build/app.js'\n * ```\n *\n * A compose healthcheck that fetches a route never goes healthy either, so the\n * container looks like it is failing to start rather than failing to serve.\n *\n * This plugin rewrites those calls into static imports of\n * `./standard-fonts/<name>.mjs`, so the font data lands in the bundle and the\n * server stays self-contained. Use it when the runtime image has **no\n * `node_modules`** — a single-file `bun build` artifact copied alone into the\n * image. When the image does keep `node_modules`, the lighter answer is to leave\n * the library external instead (`--external` it in the bundle step, and keep it\n * out of `ssr.noExternal`), which also keeps ~12 MB of\n * pdfkit out of the artifact. In Nuxt, `nxgtMaterial: { pdf: {\n * bundleStandardFonts: true } }` adds this plugin to the Nitro build.\n *\n * ```ts\n * // `@nxgt/material/vite` in a React app: the same function\n * import { pdfkitStandardFonts } from '@nxgt/material-vue/vite';\n * import { defineConfig } from 'vite';\n *\n * export default defineConfig({\n * plugins: [pdfkitStandardFonts()],\n * ssr: { noExternal: ['@nxgt/material-vue', '@nxgt/material-core'] },\n * });\n * ```\n *\n * Two things that look like fixes and are not:\n *\n * - **`resolve.alias`** mapping `#standard-fonts/` to `pdfkit/standard-fonts/`.\n * An alias rewrites specifiers the resolver is *asked* about; this one is a\n * string handed to `createRequire` at runtime, which the resolver never sees.\n * - **`--external pdfkit`**. It is a transitive dependency, so the runtime cannot\n * resolve it from the app's own directory: `Cannot find package 'pdfkit'`.\n *\n * The plugin throws if a `#standard-fonts/` reference survives the rewrite, so a\n * future pdfkit that changes the shape of those calls fails the build instead of\n * the first request.\n */\nexport function pdfkitStandardFonts(): Plugin {\n\t/**\n\t * Any call whose only argument is that literal. Deliberately not pinned to\n\t * `require$1`, which is a name rollup generated for pdfkit's own build and is\n\t * free to change on any release — there is nothing else one can do with a\n\t * `#standard-fonts/*` specifier but require it.\n\t */\n\tconst CALL = /[A-Za-z_$][\\w$]*\\(\\s*(['\"])#standard-fonts\\/(\\w+)\\1\\s*\\)/g;\n\n\treturn {\n\t\tname: 'nxgt-material:pdfkit-standard-fonts',\n\t\tenforce: 'pre',\n\t\ttransform(code, id) {\n\t\t\tif (!id.includes('pdfkit') || !code.includes('#standard-fonts/')) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst fonts = new Set<string>();\n\t\t\tconst out = code.replace(CALL, (_match, _quote, font: string) => {\n\t\t\t\tfonts.add(font);\n\t\t\t\treturn `__stdFont_${font}`;\n\t\t\t});\n\n\t\t\t// Fail the build rather than ship a server that dies on its first\n\t\t\t// request: an unmatched occurrence means pdfkit changed the shape of\n\t\t\t// these calls, and the whole point of this plugin is that nothing\n\t\t\t// downstream notices the difference until runtime.\n\t\t\tif (out.includes('#standard-fonts/')) {\n\t\t\t\tthis.error(\n\t\t\t\t\t`${id} still references '#standard-fonts/' after the transform — ` +\n\t\t\t\t\t\t'pdfkit changed how it loads its standard fonts. See the doc ' +\n\t\t\t\t\t\t'comment on pdfkitStandardFonts() in the /vite entry.',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst imports = [...fonts]\n\t\t\t\t.map(\n\t\t\t\t\t(font) =>\n\t\t\t\t\t\t`import __stdFont_${font} from './standard-fonts/${font}.mjs';`,\n\t\t\t\t)\n\t\t\t\t.join('\\n');\n\n\t\t\treturn { code: `${imports}\\n${out}`, map: null };\n\t\t},\n\t};\n}\n"
|
|
6
7
|
],
|
|
7
|
-
"mappings": ";
|
|
8
|
-
"debugId": "
|
|
8
|
+
"mappings": ";AACA,IAAM,OAAO;AA0BN,SAAS,oBAAoB,CACnC,MACA,UACsB;AAAA,EACtB,OAAO,KAAK,KAAK,IAAI,IAAI,QAAQ;AAAA;;;ACuD3B,SAAS,mBAAmB,GAAW;AAAA,EAO7C,MAAM,OAAO;AAAA,EAEb,OAAO;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS,CAAC,MAAM,IAAI;AAAA,MACnB,IAAI,CAAC,GAAG,SAAS,QAAQ,KAAK,CAAC,KAAK,SAAS,kBAAkB,GAAG;AAAA,QACjE,OAAO;AAAA,MACR;AAAA,MAEA,MAAM,QAAQ,IAAI;AAAA,MAClB,MAAM,MAAM,KAAK,QAAQ,MAAM,CAAC,QAAQ,QAAQ,SAAiB;AAAA,QAChE,MAAM,IAAI,IAAI;AAAA,QACd,OAAO,aAAa;AAAA,OACpB;AAAA,MAMD,IAAI,IAAI,SAAS,kBAAkB,GAAG;AAAA,QACrC,KAAK,MACJ,GAAG,kEACF,iEACA,sDACF;AAAA,MACD;AAAA,MAEA,MAAM,UAAU,CAAC,GAAG,KAAK,EACvB,IACA,CAAC,SACA,oBAAoB,+BAA+B,YACrD,EACC,KAAK;AAAA,CAAI;AAAA,MAEX,OAAO,EAAE,MAAM,GAAG;AAAA,EAAY,OAAO,KAAK,KAAK;AAAA;AAAA,EAEjD;AAAA;",
|
|
9
|
+
"debugId": "C84B59B7E2D9781E64756E2164756E21",
|
|
9
10
|
"names": []
|
|
10
11
|
}
|
package/docs/roadmap.md
CHANGED
|
@@ -6,7 +6,15 @@ major they target.
|
|
|
6
6
|
|
|
7
7
|
## Now
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- **The page's Content Security Policy nonce** (`/csp-nonce`) — one place
|
|
10
|
+
both libraries read the nonce from, so every `<style>` they create carries
|
|
11
|
+
it under a strict `style-src-elem`, and a `build.assetsInlineLimit`
|
|
12
|
+
(`cspAssetsInlineLimit` in `/vite`) that keeps fonts out of `data:` URIs.
|
|
13
|
+
Shipped here first; both libraries use it now.
|
|
14
|
+
|
|
15
|
+
- **Built on first use** (`/lazy-object`) — `buildOnce` and `lazyObject`,
|
|
16
|
+
with which both libraries build their zod schemas on first use rather
|
|
17
|
+
than at import, so an app under a strict CSP can configure zod first.
|
|
10
18
|
|
|
11
19
|
## Next
|
|
12
20
|
|
|
@@ -51,6 +59,11 @@ Nothing in progress right now; see Next.
|
|
|
51
59
|
|
|
52
60
|
## Shipped
|
|
53
61
|
|
|
62
|
+
- **Debounce, text matching and the async search** (`/debounce`,
|
|
63
|
+
`/text-match`, `/async-search`) — the debounce both libraries'
|
|
64
|
+
`SearchField` kept a copy of each, accent- and case-insensitive matching,
|
|
65
|
+
and a debounced search that aborts the previous run and drops a stale
|
|
66
|
+
response, behind both libraries' `Autocomplete` server search. 0.8.0.
|
|
54
67
|
- **The focus a toast's action hands back** (`/toast-focus`) — where the
|
|
55
68
|
focus was before it entered a toast, given back before the action runs, so
|
|
56
69
|
a dialog the action opens keeps the focus in both libraries. 0.7.0.
|
package/package.json
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxgt/material-core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The framework-free core of @nxgt/material and @nxgt/material-vue: the PDF document model and its react-pdf renderer, print formatters, PDF file helpers, the PDF viewer's pure modules, the currency digits, the file-size formatter, the field-blur layers, a field's first focusable control, the aria-describedby joiner, the inert background of a modal layer, Escape forwarded while an overlay closes, the Toaster's touch-hover guard and its focus hand-back, the zod issue messages, the date range presets, the file preview's kind detection and text reader, the pdfkit fonts Vite plugin and the Node-only pdfkit fonts resolution for Next.js and Nuxt builds",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "The framework-free core of @nxgt/material and @nxgt/material-vue: the PDF document model and its react-pdf renderer, print formatters, PDF file helpers, the PDF viewer's pure modules, the currency digits, the file-size formatter, the field-blur layers, a field's first focusable control, the aria-describedby joiner, the inert background of a modal layer, Escape forwarded while an overlay closes, the Toaster's touch-hover guard and its focus hand-back, the page's Content Security Policy nonce, an object built on first use, the zod issue messages, the date range presets, the debounce, accent-insensitive text matching and the race-free async search behind Autocomplete and SearchField, the file preview's kind detection and text reader, the pdfkit fonts Vite plugin, a CSP-safe Vite assetsInlineLimit and the Node-only pdfkit fonts resolution for Next.js and Nuxt builds",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"email": "stevegires@gmail.com"
|
|
8
8
|
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/softistx/nxgt-material.git",
|
|
12
|
+
"directory": "packages/core"
|
|
13
|
+
},
|
|
9
14
|
"type": "module",
|
|
10
15
|
"main": "./dist/index.js",
|
|
11
16
|
"types": "./dist/index.d.ts",
|
|
@@ -153,6 +158,31 @@
|
|
|
153
158
|
"import": "./dist/lib/date-range.js",
|
|
154
159
|
"default": "./dist/lib/date-range.js"
|
|
155
160
|
},
|
|
161
|
+
"./csp-nonce": {
|
|
162
|
+
"types": "./dist/lib/csp-nonce.d.ts",
|
|
163
|
+
"import": "./dist/lib/csp-nonce.js",
|
|
164
|
+
"default": "./dist/lib/csp-nonce.js"
|
|
165
|
+
},
|
|
166
|
+
"./lazy-object": {
|
|
167
|
+
"types": "./dist/lib/lazy-object.d.ts",
|
|
168
|
+
"import": "./dist/lib/lazy-object.js",
|
|
169
|
+
"default": "./dist/lib/lazy-object.js"
|
|
170
|
+
},
|
|
171
|
+
"./debounce": {
|
|
172
|
+
"types": "./dist/lib/debounce.d.ts",
|
|
173
|
+
"import": "./dist/lib/debounce.js",
|
|
174
|
+
"default": "./dist/lib/debounce.js"
|
|
175
|
+
},
|
|
176
|
+
"./text-match": {
|
|
177
|
+
"types": "./dist/lib/text-match.d.ts",
|
|
178
|
+
"import": "./dist/lib/text-match.js",
|
|
179
|
+
"default": "./dist/lib/text-match.js"
|
|
180
|
+
},
|
|
181
|
+
"./async-search": {
|
|
182
|
+
"types": "./dist/lib/async-search.d.ts",
|
|
183
|
+
"import": "./dist/lib/async-search.js",
|
|
184
|
+
"default": "./dist/lib/async-search.js"
|
|
185
|
+
},
|
|
156
186
|
"./file-preview": {
|
|
157
187
|
"types": "./dist/file-preview/index.d.ts",
|
|
158
188
|
"import": "./dist/file-preview/index.js",
|
|
@@ -229,6 +259,11 @@
|
|
|
229
259
|
"src/lib/slider-ticks.ts",
|
|
230
260
|
"src/lib/emoji-groups.ts",
|
|
231
261
|
"src/lib/date-range.ts",
|
|
262
|
+
"src/lib/csp-nonce.ts",
|
|
263
|
+
"src/lib/lazy-object.ts",
|
|
264
|
+
"src/lib/debounce.ts",
|
|
265
|
+
"src/lib/text-match.ts",
|
|
266
|
+
"src/lib/async-search.ts",
|
|
232
267
|
"src/file-preview/index.ts",
|
|
233
268
|
"src/pdf-viewer/index.ts",
|
|
234
269
|
"src/vite/index.ts",
|