@streamscloud/kit 0.82.0 → 0.82.2

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.
@@ -44,7 +44,7 @@ export type UploadMediaStoreOptions = {
44
44
  * 1. `add(file)` — push raw `File` into the queue (`status: 'queued'`).
45
45
  * 2. `upload()` — fetch presigned URLs from the strategy, then PUT each blob in parallel.
46
46
  * Per-file status flows `queued → uploading → success | error`; `progress` updates as
47
- * bytes are sent.
47
+ * bytes are sent. The next `upload()` retries every file left in `error`.
48
48
  * 3. Consumer wires `on.blobId` to capture each blob ID for form state, or reads `files[i].blobId`.
49
49
  *
50
50
  * @example
@@ -94,8 +94,8 @@ export declare class UploadMediaStore {
94
94
  readUrl: string;
95
95
  } | null;
96
96
  /**
97
- * Run the upload pipeline for every file currently in `'queued'` status. Files already
98
- * `uploading` / `success` / `error` are skipped. Resolves once **every** run of this store —
97
+ * Run the upload pipeline for every file currently in `'queued'` status, and retry every file in
98
+ * `'error'` status. Files already `uploading` / `success` are skipped. Resolves once **every** run of this store —
99
99
  * including ones started by earlier `upload()` calls — has reached a terminal status, so
100
100
  * `await store.upload()` always means "nothing of mine is in flight any more". Each run registers
101
101
  * itself in `AppUploadActivity` for its duration, so a mounted `UploadProgressToaster` picks it up
@@ -20,7 +20,7 @@ const revokePreviewUrl = (file) => {
20
20
  * 1. `add(file)` — push raw `File` into the queue (`status: 'queued'`).
21
21
  * 2. `upload()` — fetch presigned URLs from the strategy, then PUT each blob in parallel.
22
22
  * Per-file status flows `queued → uploading → success | error`; `progress` updates as
23
- * bytes are sent.
23
+ * bytes are sent. The next `upload()` retries every file left in `error`.
24
24
  * 3. Consumer wires `on.blobId` to capture each blob ID for form state, or reads `files[i].blobId`.
25
25
  *
26
26
  * @example
@@ -79,15 +79,15 @@ export class UploadMediaStore {
79
79
  return file?.status === 'success' ? { blobId: file.blobId, readUrl: file.readUrl } : null;
80
80
  }
81
81
  /**
82
- * Run the upload pipeline for every file currently in `'queued'` status. Files already
83
- * `uploading` / `success` / `error` are skipped. Resolves once **every** run of this store —
82
+ * Run the upload pipeline for every file currently in `'queued'` status, and retry every file in
83
+ * `'error'` status. Files already `uploading` / `success` are skipped. Resolves once **every** run of this store —
84
84
  * including ones started by earlier `upload()` calls — has reached a terminal status, so
85
85
  * `await store.upload()` always means "nothing of mine is in flight any more". Each run registers
86
86
  * itself in `AppUploadActivity` for its duration, so a mounted `UploadProgressToaster` picks it up
87
87
  * with no extra wiring.
88
88
  */
89
89
  async upload() {
90
- const queued = this._files.filter((f) => f.status === 'queued');
90
+ const queued = this._files.filter((f) => f.status === 'queued' || f.status === 'error');
91
91
  if (queued.length === 0) {
92
92
  await this.settled();
93
93
  return this.outcome();
@@ -5,3 +5,4 @@ export { handleValidationSchema, isValidHandle } from './handle-validations';
5
5
  export { numberValidationSchema, minNumberValidationSchema, isValidNumber, isValidMinNumber } from './number-validations';
6
6
  export { arrayValidationSchema, isValidArray } from './array-validations';
7
7
  export { urlValidationSchema, isValidUrl } from './url-validation';
8
+ export { isValidUuid } from './uuid-validation';
@@ -4,3 +4,4 @@ export { handleValidationSchema, isValidHandle } from './handle-validations';
4
4
  export { numberValidationSchema, minNumberValidationSchema, isValidNumber, isValidMinNumber } from './number-validations';
5
5
  export { arrayValidationSchema, isValidArray } from './array-validations';
6
6
  export { urlValidationSchema, isValidUrl } from './url-validation';
7
+ export { isValidUuid } from './uuid-validation';
@@ -0,0 +1,2 @@
1
+ /** Canonical 8-4-4-4-12 hex UUID, case-insensitive, any version or variant. */
2
+ export declare const isValidUuid: (value: string) => boolean;
@@ -0,0 +1,5 @@
1
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2
+ /** Canonical 8-4-4-4-12 hex UUID, case-insensitive, any version or variant. */
3
+ export const isValidUuid = (value) => {
4
+ return UUID_REGEX.test(value);
5
+ };
@@ -2,7 +2,7 @@
2
2
  import { IconSlot } from '../icon';
3
3
  import { formatNumeral, NumeralThousandGroupStyles, registerCursorTracker } from 'cleave-zen';
4
4
  import { untrack } from 'svelte';
5
- const { size = 'md', disabled = false, readonly = false, inert = false, error = false, borderless = false, placeholder = '', title = '', name = '', id = null, autofocus = false, min = null, max = null, decimals = 2, suffix, hideResolvedValue = false, 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel, 'aria-required': ariaRequired, icon, iconPosition = 'leading', secondaryIcon, ...mode } = $props();
5
+ const { size = 'md', disabled = false, readonly = false, inert = false, error = false, borderless = false, placeholder = '', title = '', name = '', id = null, autofocus = false, min = null, max = null, minExclusive = false, maxExclusive = false, decimals = 2, suffix, hideResolvedValue = false, 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel, 'aria-required': ariaRequired, icon, iconPosition = 'leading', secondaryIcon, ...mode } = $props();
6
6
  let inputEl = $state.raw(undefined);
7
7
  let focused = $state(false);
8
8
  let typedValue = $state(undefined);
@@ -26,11 +26,13 @@ const parseValue = (formatted) => {
26
26
  const n = parseFloat(formatted.replace(/\s/g, ''));
27
27
  return Number.isNaN(n) ? null : n;
28
28
  };
29
+ const lowerBound = $derived(min === null ? null : minExclusive ? min + 1 : min);
30
+ const upperBound = $derived(max === null ? null : maxExclusive ? max - 1 : max);
29
31
  const resolveValue = (typed) => {
30
32
  if (typed !== null) {
31
- return Math.min(Math.max(typed, min ?? -Infinity), max ?? Infinity);
33
+ return Math.min(Math.max(typed, lowerBound ?? -Infinity), upperBound ?? Infinity);
32
34
  }
33
- return mode.clearable ? null : (min ?? mode.value);
35
+ return mode.clearable ? null : (lowerBound ?? mode.value);
34
36
  };
35
37
  const resolvedTypedValue = $derived(typedValue === undefined ? undefined : resolveValue(typedValue));
36
38
  const valueHintVisible = $derived(!hideResolvedValue && resolvedTypedValue !== typedValue);
@@ -20,6 +20,10 @@ type BaseProps = {
20
20
  min?: number | null;
21
21
  /** Maximum value emitted to the consumer; the typed text is left alone until commit. */
22
22
  max?: number | null;
23
+ /** Integer bound, matching `numberValidationSchema`: an exclusive `min` resolves to `min + 1`. @default false */
24
+ minExclusive?: boolean;
25
+ /** Integer bound, matching `numberValidationSchema`: an exclusive `max` resolves to `max - 1`. @default false */
26
+ maxExclusive?: boolean;
23
27
  /** Number of decimal places shown. @default 2 */
24
28
  decimals?: number;
25
29
  /** Muted unit rendered after the value, outside the editable text (`px`, `%`, `°`). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.82.0",
3
+ "version": "0.82.2",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",