@streamscloud/kit 0.0.1-1770364570820

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.
Files changed (146) hide show
  1. package/dist/core/continuation-token.d.ts +10 -0
  2. package/dist/core/continuation-token.js +32 -0
  3. package/dist/core/css/index.d.ts +1 -0
  4. package/dist/core/css/index.js +1 -0
  5. package/dist/core/css/style-functions.d.ts +5 -0
  6. package/dist/core/css/style-functions.js +12 -0
  7. package/dist/core/cursor-result.d.ts +9 -0
  8. package/dist/core/cursor-result.js +1 -0
  9. package/dist/core/data-loaders/cursor-data-loader-with-search.svelte.d.ts +19 -0
  10. package/dist/core/data-loaders/cursor-data-loader-with-search.svelte.js +57 -0
  11. package/dist/core/data-loaders/cursor-data-loader.svelte.d.ts +13 -0
  12. package/dist/core/data-loaders/cursor-data-loader.svelte.js +33 -0
  13. package/dist/core/data-loaders/data-loader.d.ts +4 -0
  14. package/dist/core/data-loaders/data-loader.js +1 -0
  15. package/dist/core/data-loaders/index.d.ts +4 -0
  16. package/dist/core/data-loaders/index.js +4 -0
  17. package/dist/core/data-loaders/page-data-loader.svelte.d.ts +15 -0
  18. package/dist/core/data-loaders/page-data-loader.svelte.js +37 -0
  19. package/dist/core/deferred.d.ts +6 -0
  20. package/dist/core/deferred.js +13 -0
  21. package/dist/core/event-dispatcher.d.ts +9 -0
  22. package/dist/core/event-dispatcher.js +28 -0
  23. package/dist/core/files/base64-helper.d.ts +4 -0
  24. package/dist/core/files/base64-helper.js +22 -0
  25. package/dist/core/files/blob-storage.d.ts +1 -0
  26. package/dist/core/files/blob-storage.js +19 -0
  27. package/dist/core/files/file-helper.d.ts +6 -0
  28. package/dist/core/files/file-helper.js +27 -0
  29. package/dist/core/files/file-service.d.ts +4 -0
  30. package/dist/core/files/file-service.js +54 -0
  31. package/dist/core/files/file-types.d.ts +16 -0
  32. package/dist/core/files/file-types.js +28 -0
  33. package/dist/core/files/file-with-blob-data-helper.d.ts +19 -0
  34. package/dist/core/files/file-with-blob-data-helper.js +86 -0
  35. package/dist/core/files/files-provider.d.ts +8 -0
  36. package/dist/core/files/files-provider.js +38 -0
  37. package/dist/core/files/image-resizer.d.ts +31 -0
  38. package/dist/core/files/image-resizer.js +92 -0
  39. package/dist/core/files/index.d.ts +9 -0
  40. package/dist/core/files/index.js +9 -0
  41. package/dist/core/files/types.d.ts +12 -0
  42. package/dist/core/files/types.js +10 -0
  43. package/dist/core/graphql.d.ts +4 -0
  44. package/dist/core/graphql.js +10 -0
  45. package/dist/core/handle-generator.d.ts +1 -0
  46. package/dist/core/handle-generator.js +32 -0
  47. package/dist/core/i18n/index.d.ts +1 -0
  48. package/dist/core/i18n/index.js +1 -0
  49. package/dist/core/i18n/plural.d.ts +14 -0
  50. package/dist/core/i18n/plural.js +18 -0
  51. package/dist/core/index.d.ts +6 -0
  52. package/dist/core/index.js +6 -0
  53. package/dist/core/repository/index.d.ts +2 -0
  54. package/dist/core/repository/index.js +2 -0
  55. package/dist/core/repository/map-container.svelte.d.ts +9 -0
  56. package/dist/core/repository/map-container.svelte.js +22 -0
  57. package/dist/core/repository/repository-notifier.d.ts +19 -0
  58. package/dist/core/repository/repository-notifier.js +88 -0
  59. package/dist/core/repository/repository.svelte.d.ts +2 -0
  60. package/dist/core/repository/repository.svelte.js +59 -0
  61. package/dist/core/repository/types.d.ts +15 -0
  62. package/dist/core/repository/types.js +1 -0
  63. package/dist/core/theme/app-theme.svelte.d.ts +15 -0
  64. package/dist/core/theme/app-theme.svelte.js +48 -0
  65. package/dist/core/theme/index.d.ts +4 -0
  66. package/dist/core/theme/index.js +3 -0
  67. package/dist/core/theme/theme-cookie.d.ts +5 -0
  68. package/dist/core/theme/theme-cookie.js +19 -0
  69. package/dist/core/theme/types.d.ts +5 -0
  70. package/dist/core/theme/types.js +8 -0
  71. package/dist/core/toastr/index.d.ts +3 -0
  72. package/dist/core/toastr/index.js +2 -0
  73. package/dist/core/toastr/toaster-host.svelte.d.ts +5 -0
  74. package/dist/core/toastr/toaster-host.svelte.js +40 -0
  75. package/dist/core/toastr/toastr.scss +27 -0
  76. package/dist/core/toastr/toastr.svelte.d.ts +8 -0
  77. package/dist/core/toastr/toastr.svelte.js +27 -0
  78. package/dist/core/toastr/types.d.ts +14 -0
  79. package/dist/core/toastr/types.js +1 -0
  80. package/dist/core/transitions/index.d.ts +1 -0
  81. package/dist/core/transitions/index.js +1 -0
  82. package/dist/core/transitions/slide-horizontally.d.ts +8 -0
  83. package/dist/core/transitions/slide-horizontally.js +54 -0
  84. package/dist/core/utils/array-helper.d.ts +21 -0
  85. package/dist/core/utils/array-helper.js +130 -0
  86. package/dist/core/utils/base64-serializer.d.ts +4 -0
  87. package/dist/core/utils/base64-serializer.js +21 -0
  88. package/dist/core/utils/browser.d.ts +1 -0
  89. package/dist/core/utils/browser.js +1 -0
  90. package/dist/core/utils/date-helper.d.ts +51 -0
  91. package/dist/core/utils/date-helper.js +244 -0
  92. package/dist/core/utils/dom-helper.d.ts +10 -0
  93. package/dist/core/utils/dom-helper.js +34 -0
  94. package/dist/core/utils/href-validator.d.ts +22 -0
  95. package/dist/core/utils/href-validator.js +52 -0
  96. package/dist/core/utils/html-helper.d.ts +12 -0
  97. package/dist/core/utils/html-helper.js +104 -0
  98. package/dist/core/utils/index.d.ts +13 -0
  99. package/dist/core/utils/index.js +13 -0
  100. package/dist/core/utils/lazy-init.d.ts +1 -0
  101. package/dist/core/utils/lazy-init.js +7 -0
  102. package/dist/core/utils/number-helper.d.ts +5 -0
  103. package/dist/core/utils/number-helper.js +32 -0
  104. package/dist/core/utils/string-generator.d.ts +2 -0
  105. package/dist/core/utils/string-generator.js +5 -0
  106. package/dist/core/utils/string-helper.d.ts +12 -0
  107. package/dist/core/utils/string-helper.js +75 -0
  108. package/dist/core/utils/url-helper.d.ts +3 -0
  109. package/dist/core/utils/url-helper.js +13 -0
  110. package/dist/core/utils/utils.d.ts +29 -0
  111. package/dist/core/utils/utils.js +108 -0
  112. package/dist/core/validation/form-validation-handler/form-validation-handler.svelte.d.ts +47 -0
  113. package/dist/core/validation/form-validation-handler/form-validation-handler.svelte.js +182 -0
  114. package/dist/core/validation/form-validation-handler/index.d.ts +4 -0
  115. package/dist/core/validation/form-validation-handler/index.js +3 -0
  116. package/dist/core/validation/form-validation-handler/stub-form-validator.d.ts +5 -0
  117. package/dist/core/validation/form-validation-handler/stub-form-validator.js +12 -0
  118. package/dist/core/validation/form-validation-handler/types.d.ts +10 -0
  119. package/dist/core/validation/form-validation-handler/types.js +1 -0
  120. package/dist/core/validation/form-validation-handler/yup-form-validator.d.ts +11 -0
  121. package/dist/core/validation/form-validation-handler/yup-form-validator.js +49 -0
  122. package/dist/core/validation/form-validator.svelte.d.ts +12 -0
  123. package/dist/core/validation/form-validator.svelte.js +21 -0
  124. package/dist/core/validation/i-validator.d.ts +6 -0
  125. package/dist/core/validation/i-validator.js +1 -0
  126. package/dist/core/validation/index.d.ts +5 -0
  127. package/dist/core/validation/index.js +4 -0
  128. package/dist/core/validation/validation-schemas/email-validation.d.ts +3 -0
  129. package/dist/core/validation/validation-schemas/email-validation.js +17 -0
  130. package/dist/core/validation/validation-schemas/handle-validations.d.ts +3 -0
  131. package/dist/core/validation/validation-schemas/handle-validations.js +16 -0
  132. package/dist/core/validation/validation-schemas/index.d.ts +6 -0
  133. package/dist/core/validation/validation-schemas/index.js +5 -0
  134. package/dist/core/validation/validation-schemas/number-validations.d.ts +4 -0
  135. package/dist/core/validation/validation-schemas/number-validations.js +30 -0
  136. package/dist/core/validation/validation-schemas/text-validations.d.ts +4 -0
  137. package/dist/core/validation/validation-schemas/text-validations.js +19 -0
  138. package/dist/core/validation/validation-schemas/types.d.ts +19 -0
  139. package/dist/core/validation/validation-schemas/types.js +1 -0
  140. package/dist/core/validation/validation-schemas/validation-messages.d.ts +11 -0
  141. package/dist/core/validation/validation-schemas/validation-messages.js +14 -0
  142. package/dist/core/validation/validators-hub.svelte.d.ts +14 -0
  143. package/dist/core/validation/validators-hub.svelte.js +51 -0
  144. package/dist/ui/index.d.ts +1 -0
  145. package/dist/ui/index.js +2 -0
  146. package/package.json +125 -0
@@ -0,0 +1,8 @@
1
+ import type { FileWithBlobUrl } from './types';
2
+ export declare class FilesProvider {
3
+ readonly maxCacheSize: number;
4
+ private cache;
5
+ constructor(maxCacheSize?: number);
6
+ getFile(fileSrc: string | null | undefined): Promise<FileWithBlobUrl | undefined>;
7
+ clearCache(): void;
8
+ }
@@ -0,0 +1,38 @@
1
+ import { fetchFile } from './file-service';
2
+ import { FileWithBlobDataHelper } from './file-with-blob-data-helper';
3
+ export class FilesProvider {
4
+ maxCacheSize;
5
+ cache = new Map();
6
+ constructor(maxCacheSize = 3) {
7
+ this.maxCacheSize = maxCacheSize;
8
+ }
9
+ async getFile(fileSrc) {
10
+ if (!fileSrc) {
11
+ return undefined;
12
+ }
13
+ const cached = this.cache.get(fileSrc);
14
+ if (cached) {
15
+ return FileWithBlobDataHelper.fromFile(cached.file);
16
+ }
17
+ const file = await fetchFile(fileSrc);
18
+ if (!file) {
19
+ return undefined;
20
+ }
21
+ this.cache.set(fileSrc, { timestamp: Date.now(), file });
22
+ if (this.cache.size > this.maxCacheSize) {
23
+ let oldestKey = '';
24
+ let oldestTime = Infinity;
25
+ for (const [key, item] of this.cache) {
26
+ if (item.timestamp < oldestTime) {
27
+ oldestTime = item.timestamp;
28
+ oldestKey = key;
29
+ }
30
+ }
31
+ this.cache.delete(oldestKey);
32
+ }
33
+ return FileWithBlobDataHelper.fromFile(file);
34
+ }
35
+ clearCache() {
36
+ this.cache.clear();
37
+ }
38
+ }
@@ -0,0 +1,31 @@
1
+ import type { BlobWithName } from './types';
2
+ export declare const resizeImage: <T extends File>(file: T, limits: {
3
+ max1: number;
4
+ max2: number;
5
+ }) => Promise<BlobWithName>;
6
+ /**
7
+ * Resizes an image while maintaining its aspect ratio, ensuring that both dimensions fit within the given constraints.
8
+ *
9
+ * The function takes two numeric constraints (`max1` and `max2`) and determines which one applies to the long side
10
+ * and which one applies to the short side of the image.
11
+ *
12
+ * - If the image already fits within the constraints (both sides are within their respective limits),
13
+ * it is returned unchanged.
14
+ * - If both constraints are provided, the image is resized so that the long side matches its limit.
15
+ * However, if this causes the short side to exceed its limit, the short side constraint is applied instead.
16
+ * - If only one constraint is provided, it is applied to the longer side, and the other side is scaled proportionally.
17
+ * - If both constraints are zero or negative, the original image is returned without modifications.
18
+ *
19
+ * @param blob - The original image as a `Blob` object.
20
+ * @param limits - An object containing two numeric constraints, `max1` and `max2`.
21
+ * - **`max1`** - One of the two size constraints.
22
+ * - **`max2`** - The second size constraint.
23
+ * **Note:** `max1` and `max2` are not tied to specific dimensions (width or height).
24
+ * The function determines which one applies to the long side and which to the short side.
25
+ *
26
+ * @returns A promise that resolves to a new resized `Blob` object, or the original `Blob` if resizing is not needed.
27
+ */
28
+ export declare const resizeBlob: <T extends Blob>(blob: T, limits: {
29
+ max1: number;
30
+ max2: number;
31
+ }) => Promise<Blob>;
@@ -0,0 +1,92 @@
1
+ let cachedCanvas = null;
2
+ const getCanvas = () => (cachedCanvas ??= document.createElement('canvas'));
3
+ export const resizeImage = async (file, limits) => {
4
+ const blob = await resizeBlob(file, limits);
5
+ const result = blob;
6
+ if (!(blob instanceof File)) {
7
+ result.name = file.name;
8
+ }
9
+ return result;
10
+ };
11
+ /**
12
+ * Resizes an image while maintaining its aspect ratio, ensuring that both dimensions fit within the given constraints.
13
+ *
14
+ * The function takes two numeric constraints (`max1` and `max2`) and determines which one applies to the long side
15
+ * and which one applies to the short side of the image.
16
+ *
17
+ * - If the image already fits within the constraints (both sides are within their respective limits),
18
+ * it is returned unchanged.
19
+ * - If both constraints are provided, the image is resized so that the long side matches its limit.
20
+ * However, if this causes the short side to exceed its limit, the short side constraint is applied instead.
21
+ * - If only one constraint is provided, it is applied to the longer side, and the other side is scaled proportionally.
22
+ * - If both constraints are zero or negative, the original image is returned without modifications.
23
+ *
24
+ * @param blob - The original image as a `Blob` object.
25
+ * @param limits - An object containing two numeric constraints, `max1` and `max2`.
26
+ * - **`max1`** - One of the two size constraints.
27
+ * - **`max2`** - The second size constraint.
28
+ * **Note:** `max1` and `max2` are not tied to specific dimensions (width or height).
29
+ * The function determines which one applies to the long side and which to the short side.
30
+ *
31
+ * @returns A promise that resolves to a new resized `Blob` object, or the original `Blob` if resizing is not needed.
32
+ */
33
+ export const resizeBlob = (blob, limits) => {
34
+ const { max1, max2 } = limits;
35
+ if (max1 <= 0 && max2 <= 0) {
36
+ return Promise.resolve(blob);
37
+ }
38
+ return new Promise((resolve, reject) => {
39
+ const image = new Image();
40
+ image.src = URL.createObjectURL(blob);
41
+ image.onload = () => {
42
+ const { width, height } = image;
43
+ const isHorizontal = width > height;
44
+ const originalLongSide = isHorizontal ? width : height;
45
+ const originalShortSide = isHorizontal ? height : width;
46
+ const [longSideMax, shortSideMax] = max1 >= max2 ? [max1, max2] : [max2, max1];
47
+ if (longSideMax > 0 && originalLongSide <= longSideMax && shortSideMax > 0 && originalShortSide <= shortSideMax) {
48
+ URL.revokeObjectURL(image.src);
49
+ return resolve(blob);
50
+ }
51
+ let newLongSide = Math.min(originalLongSide, longSideMax);
52
+ let newShortSide = originalShortSide * (newLongSide / originalLongSide);
53
+ if (shortSideMax > 0 && newShortSide > shortSideMax) {
54
+ const reducer = newShortSide / shortSideMax;
55
+ newShortSide = shortSideMax;
56
+ newLongSide = newLongSide / reducer;
57
+ }
58
+ newLongSide = Math.floor(newLongSide);
59
+ newShortSide = Math.floor(newShortSide);
60
+ // Skip resize if dimensions unchanged (avoids lossy re-encoding)
61
+ if (newLongSide === originalLongSide && newShortSide === originalShortSide) {
62
+ URL.revokeObjectURL(image.src);
63
+ return resolve(blob);
64
+ }
65
+ const newWidth = isHorizontal ? newLongSide : newShortSide;
66
+ const newHeight = isHorizontal ? newShortSide : newLongSide;
67
+ const canvas = getCanvas();
68
+ canvas.width = newWidth;
69
+ canvas.height = newHeight;
70
+ const ctx = canvas.getContext('2d');
71
+ if (!ctx) {
72
+ URL.revokeObjectURL(image.src);
73
+ return reject(new Error('Failed to get canvas context'));
74
+ }
75
+ ctx.drawImage(image, 0, 0, newWidth, newHeight);
76
+ canvas.toBlob((resizedBlob) => {
77
+ URL.revokeObjectURL(image.src);
78
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
79
+ if (resizedBlob) {
80
+ resolve(resizedBlob);
81
+ }
82
+ else {
83
+ reject(new Error('Failed to create blob from canvas'));
84
+ }
85
+ }, blob.type);
86
+ };
87
+ image.onerror = () => {
88
+ URL.revokeObjectURL(image.src);
89
+ reject(new Error('Failed to load image'));
90
+ };
91
+ });
92
+ };
@@ -0,0 +1,9 @@
1
+ export type { BlobWithName, FileWithBlobUrl, FileWithUploadUrl } from './types';
2
+ export { toBlobWithName, toFileWithUploadUrl } from './types';
3
+ export { uploadBlob } from './blob-storage';
4
+ export { FileHelper } from './file-helper';
5
+ export { downloadBlob, downloadFromUrl, downloadJson, fetchFile } from './file-service';
6
+ export { AcceptFileType, matchesAcceptedFileTypes } from './file-types';
7
+ export { resizeBlob, resizeImage } from './image-resizer';
8
+ export { FileWithBlobDataHelper } from './file-with-blob-data-helper';
9
+ export { FilesProvider } from './files-provider';
@@ -0,0 +1,9 @@
1
+ // Utilities
2
+ export { toBlobWithName, toFileWithUploadUrl } from './types';
3
+ export { uploadBlob } from './blob-storage';
4
+ export { FileHelper } from './file-helper';
5
+ export { downloadBlob, downloadFromUrl, downloadJson, fetchFile } from './file-service';
6
+ export { AcceptFileType, matchesAcceptedFileTypes } from './file-types';
7
+ export { resizeBlob, resizeImage } from './image-resizer';
8
+ export { FileWithBlobDataHelper } from './file-with-blob-data-helper';
9
+ export { FilesProvider } from './files-provider';
@@ -0,0 +1,12 @@
1
+ export type FileWithBlobUrl = File & {
2
+ blobUrl: string;
3
+ id: string;
4
+ };
5
+ export type FileWithUploadUrl<T extends File = File> = T & {
6
+ uploadUrl: string;
7
+ };
8
+ export declare const toFileWithUploadUrl: <T extends File>(file: T, uploadUrl: string) => FileWithUploadUrl<T>;
9
+ export type BlobWithName = Blob & {
10
+ name: string;
11
+ };
12
+ export declare const toBlobWithName: (blob: Blob, name: string) => BlobWithName;
@@ -0,0 +1,10 @@
1
+ export const toFileWithUploadUrl = (file, uploadUrl) => {
2
+ const fileWithUploadUrl = file;
3
+ fileWithUploadUrl.uploadUrl = uploadUrl;
4
+ return fileWithUploadUrl;
5
+ };
6
+ export const toBlobWithName = (blob, name) => {
7
+ const blobWithName = blob;
8
+ blobWithName.name = name;
9
+ return blobWithName;
10
+ };
@@ -0,0 +1,4 @@
1
+ export declare const createGQLClient: (data: {
2
+ url: string;
3
+ customFetch?: typeof fetch;
4
+ }) => import("@urql/core").Client;
@@ -0,0 +1,10 @@
1
+ import { createClient, fetchExchange } from '@urql/core';
2
+ export const createGQLClient = (data) => createClient({
3
+ url: data.url,
4
+ requestPolicy: 'network-only',
5
+ fetchOptions: {
6
+ credentials: 'include'
7
+ },
8
+ fetch: data.customFetch ?? fetch,
9
+ exchanges: [fetchExchange]
10
+ });
@@ -0,0 +1 @@
1
+ export declare const generateHandle: (base: string | null | undefined) => string;
@@ -0,0 +1,32 @@
1
+ const SPEC_SYMBOLS = [
2
+ { symbol: 'å', replacement: 'a' },
3
+ { symbol: 'Å', replacement: 'A' },
4
+ { symbol: 'æ', replacement: 'ae' },
5
+ { symbol: 'Æ', replacement: 'Ae' },
6
+ { symbol: 'ø', replacement: 'o' },
7
+ { symbol: 'Ø', replacement: 'O' },
8
+ { symbol: 'ö', replacement: 'o' },
9
+ { symbol: 'Ö', replacement: 'O' },
10
+ { symbol: 'ä', replacement: 'a' },
11
+ { symbol: 'Ä', replacement: 'A' }
12
+ ];
13
+ export const generateHandle = (base) => {
14
+ if (!base) {
15
+ return '';
16
+ }
17
+ let handle = base;
18
+ // Replace nordic/special chars
19
+ for (const { symbol, replacement } of SPEC_SYMBOLS) {
20
+ handle = handle.replace(new RegExp(symbol, 'g'), replacement);
21
+ }
22
+ handle = handle
23
+ // Insert hyphen before uppercase (camelCase → kebab-case)
24
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
25
+ // Lowercase
26
+ .toLowerCase()
27
+ // Replace any non-alphanumeric with hyphen
28
+ .replace(/[^a-z0-9]+/g, '-')
29
+ // Remove leading/trailing hyphens
30
+ .replace(/^-+|-+$/g, '');
31
+ return handle;
32
+ };
@@ -0,0 +1 @@
1
+ export { plural } from './plural';
@@ -0,0 +1 @@
1
+ export { plural } from './plural';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Plural function for wuchale i18n.
3
+ * Wuchale extracts the candidates array and compiles locale-specific plural rules.
4
+ *
5
+ * @param count - The number to pluralize
6
+ * @param candidates - Array of plural forms, e.g., ['One item', '# items']
7
+ * @param rule - Optional plural rule function (injected by wuchale at compile time)
8
+ * @returns The appropriate plural form with # replaced by count
9
+ *
10
+ * @example
11
+ * plural(1, ['# item', '# items']) // "1 item"
12
+ * plural(5, ['# item', '# items']) // "5 items"
13
+ */
14
+ export declare const plural: (count: number, candidates: string[], rule?: (n: number) => number) => string;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Plural function for wuchale i18n.
3
+ * Wuchale extracts the candidates array and compiles locale-specific plural rules.
4
+ *
5
+ * @param count - The number to pluralize
6
+ * @param candidates - Array of plural forms, e.g., ['One item', '# items']
7
+ * @param rule - Optional plural rule function (injected by wuchale at compile time)
8
+ * @returns The appropriate plural form with # replaced by count
9
+ *
10
+ * @example
11
+ * plural(1, ['# item', '# items']) // "1 item"
12
+ * plural(5, ['# item', '# items']) // "5 items"
13
+ */
14
+ export const plural = (count, candidates, rule = (n) => (n === 1 ? 0 : 1)) => {
15
+ const index = rule(count);
16
+ const template = candidates[index] ?? candidates[candidates.length - 1];
17
+ return template.replace(/#/g, String(count));
18
+ };
@@ -0,0 +1,6 @@
1
+ export type { CursorPayload, CursorResult } from './cursor-result';
2
+ export { ContinuationToken } from './continuation-token';
3
+ export { Deferred } from './deferred';
4
+ export { AppEvent, EventDispatcher } from './event-dispatcher';
5
+ export { generateHandle } from './handle-generator';
6
+ export { createGQLClient } from './graphql';
@@ -0,0 +1,6 @@
1
+ // Utilities
2
+ export { ContinuationToken } from './continuation-token';
3
+ export { Deferred } from './deferred';
4
+ export { AppEvent, EventDispatcher } from './event-dispatcher';
5
+ export { generateHandle } from './handle-generator';
6
+ export { createGQLClient } from './graphql';
@@ -0,0 +1,2 @@
1
+ export type { Repository } from './types';
2
+ export { createRepository } from './repository.svelte';
@@ -0,0 +1,2 @@
1
+ // Factory
2
+ export { createRepository } from './repository.svelte';
@@ -0,0 +1,9 @@
1
+ import type { WithId } from './types';
2
+ export declare class MapContainer<T extends WithId> {
3
+ private _container;
4
+ get items(): T[];
5
+ add(item: T): void;
6
+ remove(item: T): void;
7
+ set(items: T[]): void;
8
+ clear(): void;
9
+ }
@@ -0,0 +1,22 @@
1
+ import { SvelteMap } from 'svelte/reactivity';
2
+ export class MapContainer {
3
+ _container = new SvelteMap();
4
+ get items() {
5
+ return Array.from(this._container.values());
6
+ }
7
+ add(item) {
8
+ this._container.set(item.id, item);
9
+ }
10
+ remove(item) {
11
+ this._container.delete(item.id);
12
+ }
13
+ set(items) {
14
+ this._container.clear();
15
+ for (const item of items) {
16
+ this._container.set(item.id, item);
17
+ }
18
+ }
19
+ clear() {
20
+ this._container.clear();
21
+ }
22
+ }
@@ -0,0 +1,19 @@
1
+ import type { WithId } from './types';
2
+ export declare class RepositoryNotifier<T extends WithId> {
3
+ private _changeSubscribers;
4
+ private _addSubscribers;
5
+ private _removeSubscribers;
6
+ private _clearSubscribers;
7
+ private _isChangeNotificationPending;
8
+ private _pendingAddItems;
9
+ private _pendingRemoveItems;
10
+ private _isClearNotificationPending;
11
+ notifyChange(getItems: () => T[]): void;
12
+ notifyAdd(items: T[]): void;
13
+ notifyRemove(items: T[]): void;
14
+ notifyClear(): void;
15
+ subscribeOnChange(callback: (item: T[]) => void): () => void;
16
+ subscribeOnAdd(callback: (item: T[]) => void): () => void;
17
+ subscribeOnRemove(callback: (item: T[]) => void): () => void;
18
+ subscribeOnClear(callback: () => void): () => void;
19
+ }
@@ -0,0 +1,88 @@
1
+ // queueMicrotask defers notifications, preventing re-entrant update loops
2
+ export class RepositoryNotifier {
3
+ _changeSubscribers = new Set();
4
+ _addSubscribers = new Set();
5
+ _removeSubscribers = new Set();
6
+ _clearSubscribers = new Set();
7
+ _isChangeNotificationPending = false;
8
+ _pendingAddItems = [];
9
+ _pendingRemoveItems = [];
10
+ _isClearNotificationPending = false;
11
+ notifyChange(getItems) {
12
+ if (this._changeSubscribers.size === 0) {
13
+ return;
14
+ }
15
+ if (this._isChangeNotificationPending) {
16
+ return;
17
+ }
18
+ this._isChangeNotificationPending = true;
19
+ queueMicrotask(() => {
20
+ this._isChangeNotificationPending = false;
21
+ const currentItems = getItems();
22
+ this._changeSubscribers.forEach((x) => x(currentItems));
23
+ });
24
+ }
25
+ notifyAdd(items) {
26
+ if (this._addSubscribers.size === 0) {
27
+ return;
28
+ }
29
+ if (items.length === 0) {
30
+ return;
31
+ }
32
+ const wasEmpty = this._pendingAddItems.length === 0;
33
+ this._pendingAddItems.push(...items);
34
+ if (wasEmpty) {
35
+ queueMicrotask(() => {
36
+ const itemsToNotify = [...this._pendingAddItems];
37
+ this._pendingAddItems = [];
38
+ this._addSubscribers.forEach((x) => x(itemsToNotify));
39
+ });
40
+ }
41
+ }
42
+ notifyRemove(items) {
43
+ if (this._removeSubscribers.size === 0) {
44
+ return;
45
+ }
46
+ if (items.length === 0) {
47
+ return;
48
+ }
49
+ const wasEmpty = this._pendingRemoveItems.length === 0;
50
+ this._pendingRemoveItems.push(...items);
51
+ if (wasEmpty) {
52
+ queueMicrotask(() => {
53
+ const itemsToNotify = [...this._pendingRemoveItems];
54
+ this._pendingRemoveItems = [];
55
+ this._removeSubscribers.forEach((x) => x(itemsToNotify));
56
+ });
57
+ }
58
+ }
59
+ notifyClear() {
60
+ if (this._clearSubscribers.size === 0) {
61
+ return;
62
+ }
63
+ if (this._isClearNotificationPending) {
64
+ return;
65
+ }
66
+ this._isClearNotificationPending = true;
67
+ queueMicrotask(() => {
68
+ this._isClearNotificationPending = false;
69
+ this._clearSubscribers.forEach((x) => x());
70
+ });
71
+ }
72
+ subscribeOnChange(callback) {
73
+ this._changeSubscribers.add(callback);
74
+ return () => this._changeSubscribers.delete(callback);
75
+ }
76
+ subscribeOnAdd(callback) {
77
+ this._addSubscribers.add(callback);
78
+ return () => this._addSubscribers.delete(callback);
79
+ }
80
+ subscribeOnRemove(callback) {
81
+ this._removeSubscribers.add(callback);
82
+ return () => this._removeSubscribers.delete(callback);
83
+ }
84
+ subscribeOnClear(callback) {
85
+ this._clearSubscribers.add(callback);
86
+ return () => this._clearSubscribers.delete(callback);
87
+ }
88
+ }
@@ -0,0 +1,2 @@
1
+ import type { Repository, WithId } from './types';
2
+ export declare const createRepository: <T extends WithId>(limit?: number) => Repository<T>;
@@ -0,0 +1,59 @@
1
+ import { MapContainer } from './map-container.svelte';
2
+ import { RepositoryNotifier } from './repository-notifier';
3
+ export const createRepository = (limit) => {
4
+ return new RepositoryInstance(limit);
5
+ };
6
+ class RepositoryInstance {
7
+ _container = new MapContainer();
8
+ _limit;
9
+ _notifier = new RepositoryNotifier();
10
+ constructor(limit) {
11
+ this._limit = limit;
12
+ }
13
+ get items() {
14
+ return this._container.items;
15
+ }
16
+ get limit() {
17
+ return this._limit;
18
+ }
19
+ add(...items) {
20
+ const itemsToNotify = [];
21
+ for (const item of items) {
22
+ if (!this._limit || this.items.length < this._limit) {
23
+ this._container.add(item);
24
+ itemsToNotify.push(item);
25
+ }
26
+ }
27
+ this._notifier.notifyAdd(itemsToNotify);
28
+ this._notifier.notifyChange(() => this.items);
29
+ }
30
+ set(...items) {
31
+ const itemsToSet = items.slice(0, this.limit);
32
+ this._container.set(itemsToSet);
33
+ this._notifier.notifyChange(() => this.items);
34
+ }
35
+ remove(...items) {
36
+ for (const item of items) {
37
+ this._container.remove(item);
38
+ }
39
+ this._notifier.notifyRemove(items);
40
+ this._notifier.notifyChange(() => this.items);
41
+ }
42
+ clear() {
43
+ this._container.clear();
44
+ this._notifier.notifyClear();
45
+ this._notifier.notifyChange(() => this.items);
46
+ }
47
+ subscribeOnChange(callback) {
48
+ return this._notifier.subscribeOnChange(callback);
49
+ }
50
+ subscribeOnAdd(callback) {
51
+ return this._notifier.subscribeOnAdd(callback);
52
+ }
53
+ subscribeOnRemove(callback) {
54
+ return this._notifier.subscribeOnRemove(callback);
55
+ }
56
+ subscribeOnClear(callback) {
57
+ return this._notifier.subscribeOnClear(callback);
58
+ }
59
+ }
@@ -0,0 +1,15 @@
1
+ export type WithId = {
2
+ id: string;
3
+ };
4
+ export interface Repository<T extends WithId> {
5
+ items: T[];
6
+ limit?: number;
7
+ add(...item: T[]): void;
8
+ remove(...item: T[]): void;
9
+ set(...item: T[]): void;
10
+ clear(): void;
11
+ subscribeOnChange(callback: (item: T[]) => void): () => void;
12
+ subscribeOnAdd(callback: (item: T[]) => void): () => void;
13
+ subscribeOnRemove(callback: (item: T[]) => void): () => void;
14
+ subscribeOnClear(callback: () => void): () => void;
15
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { type AppThemeValue } from './types';
2
+ declare class AppThemeContainer {
3
+ private _active;
4
+ private _mediaQuery;
5
+ private _isDarkMode;
6
+ get active(): AppThemeValue;
7
+ /** Whether dark mode is currently active (resolves 'auto' based on system preference) */
8
+ get isDarkMode(): boolean;
9
+ set active(value: AppThemeValue);
10
+ init: (serverTheme?: AppThemeValue) => void;
11
+ toggle: () => void;
12
+ private applyToDocument;
13
+ }
14
+ export declare const AppTheme: AppThemeContainer;
15
+ export {};
@@ -0,0 +1,48 @@
1
+ import { isBrowser } from '../utils';
2
+ import { ThemeCookie } from './theme-cookie';
3
+ import { getColorScheme } from './types';
4
+ class AppThemeContainer {
5
+ _active = $state('auto');
6
+ _mediaQuery = null;
7
+ _isDarkMode = $state(false);
8
+ get active() {
9
+ return this._active;
10
+ }
11
+ /** Whether dark mode is currently active (resolves 'auto' based on system preference) */
12
+ get isDarkMode() {
13
+ return this._isDarkMode;
14
+ }
15
+ set active(value) {
16
+ this._active = value;
17
+ ThemeCookie.set(value);
18
+ this.applyToDocument();
19
+ }
20
+ init = (serverTheme) => {
21
+ this._active = serverTheme ?? ThemeCookie.get() ?? 'auto';
22
+ if (isBrowser()) {
23
+ this._mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
24
+ this._mediaQuery.addEventListener('change', () => {
25
+ if (this._active === 'auto') {
26
+ this.applyToDocument();
27
+ }
28
+ });
29
+ }
30
+ this.applyToDocument();
31
+ };
32
+ toggle = () => {
33
+ this.active = this.isDarkMode ? 'light' : 'dark';
34
+ };
35
+ applyToDocument = () => {
36
+ if (!isBrowser()) {
37
+ return;
38
+ }
39
+ if (this._active === 'auto') {
40
+ this._isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
41
+ }
42
+ else {
43
+ this._isDarkMode = this._active === 'dark';
44
+ }
45
+ document.documentElement.style.colorScheme = getColorScheme(this._active);
46
+ };
47
+ }
48
+ export const AppTheme = new AppThemeContainer();
@@ -0,0 +1,4 @@
1
+ export { AppTheme } from './app-theme.svelte';
2
+ export { ThemeCookie } from './theme-cookie';
3
+ export { APP_THEME_COOKIE, APP_THEME_VALUES, isValidTheme, getColorScheme } from './types';
4
+ export type { AppThemeValue } from './types';
@@ -0,0 +1,3 @@
1
+ export { AppTheme } from './app-theme.svelte';
2
+ export { ThemeCookie } from './theme-cookie';
3
+ export { APP_THEME_COOKIE, APP_THEME_VALUES, isValidTheme, getColorScheme } from './types';
@@ -0,0 +1,5 @@
1
+ import { type AppThemeValue } from './types';
2
+ export declare const ThemeCookie: {
3
+ get: () => AppThemeValue | null;
4
+ set: (value: AppThemeValue) => void;
5
+ };