diff --git a/.vscode/settings.json b/.vscode/settings.json
index 55d62472..d5288243 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -86,6 +86,75 @@
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000,
"files.exclude": {
+ ".DS_Store": true,
+ ".changeset": true,
+ ".dockerignore": true,
+ ".editorconfig": true,
+ ".env": true,
+ ".env.example": true,
+ ".env.prod": true,
+ ".env.prod.example": true,
+ ".git": true,
+ ".gitattributes": true,
+ ".github": true,
+ ".gitignore": true,
+ ".markdownlintignore": true,
+ ".markuplintrc.cjs": true,
+ ".nhost": true,
+ ".npmrc": true,
+ ".secrets": true,
+ ".secrets.example": true,
+ ".secrets.prod": true,
+ ".secrets.prod.example": true,
+ ".textlintignore": true,
+ ".turbo": true,
+ ".vercelignore": true,
+ ".vscode": true,
+ "CHANGELOG.md": true,
+ "CODE_OF_CONDUCT.md": true,
+ "CONTRIBUTING.md": true,
+ "DEVELOPERS.md": true,
+ "Dockerfile": true,
+ "LICENSE": true,
+ "Makefile": true,
+ "README.md": true,
+ "biome.jsonc": true,
+ "bun.Dockerfile": true,
+ "bun.lockb": true,
+ "cog.toml": true,
+ "compose.override.yml": true,
+ "compose.yml": true,
+ "config": true,
+ "config.yaml": true,
+ "degit.json": true,
+ "docs": true,
+ "flake.nix": true,
+ "functions": true,
+ "infra": true,
+ "nhost": true,
+ "node_modules": true,
+ "package.json": true,
+ "packages": true,
+ "patches": true,
+ "pnpm-lock.yaml": true,
+ "pnpm-workspace.yaml": true,
+ "scripts": true,
+ "turbo.json": true,
+ "vercel.json": true,
+ "vitest.config.ts": true,
+ "vitest.workspaces.ts": true,
+ "apps/console-fb": true,
+ "apps/console-sc": true,
+ "apps/docs": true,
+ "apps/playground": true,
+ "apps/web": true,
+ "packages/assets": true,
+ "packages/biome-config": true,
+ "packages/role-houdini": true,
+ "packages/skeleton-ui": true,
+ "packages/typescript-config": true,
+ "packages/ui": true,
+ "packages/utils": true,
"**/.git": true,
"**/.DS_Store": true,
".idea": true,
@@ -318,5 +387,81 @@
"[yaml]": {
"editor.defaultFormatter": "redhat.vscode-yaml"
},
- "redhat.telemetry.enabled": false
+ "redhat.telemetry.enabled": false,
+ "monorepoFocusWorkspace.internal": {
+ "spectacular": {
+ "managedFilesIgnoreEntries": [
+ ".DS_Store",
+ ".changeset",
+ ".dockerignore",
+ ".editorconfig",
+ ".env",
+ ".env.example",
+ ".env.prod",
+ ".env.prod.example",
+ ".git",
+ ".gitattributes",
+ ".github",
+ ".gitignore",
+ ".markdownlintignore",
+ ".markuplintrc.cjs",
+ ".nhost",
+ ".npmrc",
+ ".secrets",
+ ".secrets.example",
+ ".secrets.prod",
+ ".secrets.prod.example",
+ ".textlintignore",
+ ".turbo",
+ ".vercelignore",
+ ".vscode",
+ "CHANGELOG.md",
+ "CODE_OF_CONDUCT.md",
+ "CONTRIBUTING.md",
+ "DEVELOPERS.md",
+ "Dockerfile",
+ "LICENSE",
+ "Makefile",
+ "README.md",
+ "biome.jsonc",
+ "bun.Dockerfile",
+ "bun.lockb",
+ "cog.toml",
+ "compose.override.yml",
+ "compose.yml",
+ "config",
+ "config.yaml",
+ "degit.json",
+ "docs",
+ "flake.nix",
+ "functions",
+ "infra",
+ "nhost",
+ "node_modules",
+ "package.json",
+ "packages",
+ "patches",
+ "pnpm-lock.yaml",
+ "pnpm-workspace.yaml",
+ "scripts",
+ "turbo.json",
+ "vercel.json",
+ "vitest.config.ts",
+ "vitest.workspaces.ts",
+ "apps/console-fb",
+ "apps/console-sc",
+ "apps/docs",
+ "apps/playground",
+ "apps/web",
+ "packages/assets",
+ "packages/biome-config",
+ "packages/role-houdini",
+ "packages/skeleton-ui",
+ "packages/typescript-config",
+ "packages/ui",
+ "packages/utils"
+ ],
+ "focusedWorkspace": "console"
+ }
+ }
}
diff --git a/apps/console/src/lib/api/index.ts b/apps/console/src/lib/api/index.ts
deleted file mode 100644
index 90f6e792..00000000
--- a/apps/console/src/lib/api/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { loadOptions } from './svelte_select_fetches';
diff --git a/apps/console/src/lib/components/form/Checkbox.svelte b/apps/console/src/lib/components/form/Checkbox.svelte
deleted file mode 100644
index 8ef41aba..00000000
--- a/apps/console/src/lib/components/form/Checkbox.svelte
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/CheckboxFB.svelte b/apps/console/src/lib/components/form/CheckboxFB.svelte
deleted file mode 100644
index a82456cb..00000000
--- a/apps/console/src/lib/components/form/CheckboxFB.svelte
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/DateInput.svelte b/apps/console/src/lib/components/form/DateInput.svelte
deleted file mode 100644
index 485c7cd4..00000000
--- a/apps/console/src/lib/components/form/DateInput.svelte
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-{#if $errors}
- {$errors}
-{/if}
-
-
diff --git a/apps/console/src/lib/components/form/DateInput.svelte.bak b/apps/console/src/lib/components/form/DateInput.svelte.bak
deleted file mode 100644
index a3e48d4d..00000000
--- a/apps/console/src/lib/components/form/DateInput.svelte.bak
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
- {#if error}
-
- Oh, snapp! {error}
- {/if}
-
diff --git a/apps/console/src/lib/components/form/DeleteForm.svelte b/apps/console/src/lib/components/form/DeleteForm.svelte
deleted file mode 100644
index a229712d..00000000
--- a/apps/console/src/lib/components/form/DeleteForm.svelte
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
diff --git a/apps/console/src/lib/components/form/ErrorMessage.svelte b/apps/console/src/lib/components/form/ErrorMessage.svelte
deleted file mode 100644
index 6dcb9702..00000000
--- a/apps/console/src/lib/components/form/ErrorMessage.svelte
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-{#if error}
-
- Oh, snapp!
- {error}
-
-{/if}
diff --git a/apps/console/src/lib/components/form/Fieldset.svelte b/apps/console/src/lib/components/form/Fieldset.svelte
deleted file mode 100644
index d0b6d172..00000000
--- a/apps/console/src/lib/components/form/Fieldset.svelte
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
{name}
-
-
diff --git a/apps/console/src/lib/components/form/FloatingTextInput.svelte b/apps/console/src/lib/components/form/FloatingTextInput.svelte
deleted file mode 100644
index 4aca65c8..00000000
--- a/apps/console/src/lib/components/form/FloatingTextInput.svelte
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
- {label}
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/Form.svelte b/apps/console/src/lib/components/form/Form.svelte
deleted file mode 100644
index 3142ffcd..00000000
--- a/apps/console/src/lib/components/form/Form.svelte
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
diff --git a/apps/console/src/lib/components/form/FormAlerts.svelte b/apps/console/src/lib/components/form/FormAlerts.svelte
deleted file mode 100644
index 89bbfb47..00000000
--- a/apps/console/src/lib/components/form/FormAlerts.svelte
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-{#if message || errors}
- = 400 ? 'red' : 'blue'} dismissable={false} class="!items-start">
-
- {$page.status >= 400 ? 'Error' : 'Info'}
-
- {message ?? 'Got Errors:'}
- {#if errors}
-
- {#each errors as error, index}
- - {error}
- {/each}
-
-
- {/if}
-
-{/if}
diff --git a/apps/console/src/lib/components/form/Radio.svelte b/apps/console/src/lib/components/form/Radio.svelte
deleted file mode 100644
index b06713db..00000000
--- a/apps/console/src/lib/components/form/Radio.svelte
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-{#if label}
-
-{/if}
-
- {#each items as item}
-
- {/each}
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/RadioFB.svelte b/apps/console/src/lib/components/form/RadioFB.svelte
deleted file mode 100644
index 75a3a934..00000000
--- a/apps/console/src/lib/components/form/RadioFB.svelte
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
-
-
-{#if label}
-
-{/if}
-
- {#each items as item}
- {@const id = generateId()}
-
-
- {item.label}
-
- {/each}
-
-
-{#if $errors}
- {$errors}
-{/if}
-
-
diff --git a/apps/console/src/lib/components/form/RangeDU.svelte b/apps/console/src/lib/components/form/RangeDU.svelte
deleted file mode 100644
index e21b7805..00000000
--- a/apps/console/src/lib/components/form/RangeDU.svelte
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/RangeFB.svelte b/apps/console/src/lib/components/form/RangeFB.svelte
deleted file mode 100644
index 7e1654e7..00000000
--- a/apps/console/src/lib/components/form/RangeFB.svelte
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/Select.svelte b/apps/console/src/lib/components/form/Select.svelte
deleted file mode 100644
index bb71f437..00000000
--- a/apps/console/src/lib/components/form/Select.svelte
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-{#if label}
-
-{/if}
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/SelectDU.svelte b/apps/console/src/lib/components/form/SelectDU.svelte
deleted file mode 100644
index 6bc89452..00000000
--- a/apps/console/src/lib/components/form/SelectDU.svelte
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-{#if label}
-
-{/if}
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/SelectFB.svelte b/apps/console/src/lib/components/form/SelectFB.svelte
deleted file mode 100644
index c7446b81..00000000
--- a/apps/console/src/lib/components/form/SelectFB.svelte
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/TagsInput.svelte b/apps/console/src/lib/components/form/TagsInput.svelte
deleted file mode 100644
index 88e44b6f..00000000
--- a/apps/console/src/lib/components/form/TagsInput.svelte
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-{#if $errors}
- {#each Object.entries($errors) as [pos, errs]}
- {#if pos == '_errors'}
- {errs}
- {:else}
- {pos}: {errs}
- {/if}
- {/each}
-{/if}
-
-
diff --git a/apps/console/src/lib/components/form/TextInput.svelte b/apps/console/src/lib/components/form/TextInput.svelte
deleted file mode 100644
index 5c27bc80..00000000
--- a/apps/console/src/lib/components/form/TextInput.svelte
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/Textarea.svelte b/apps/console/src/lib/components/form/Textarea.svelte
deleted file mode 100644
index 0cc69ab6..00000000
--- a/apps/console/src/lib/components/form/Textarea.svelte
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-{#if label}
-
-{/if}
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/ToggleFB.svelte b/apps/console/src/lib/components/form/ToggleFB.svelte
deleted file mode 100644
index 7a25e8b6..00000000
--- a/apps/console/src/lib/components/form/ToggleFB.svelte
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-{#if $errors}
- {$errors}
-{/if}
diff --git a/apps/console/src/lib/components/form/forms.ts b/apps/console/src/lib/components/form/forms.ts
deleted file mode 100644
index f8469aa3..00000000
--- a/apps/console/src/lib/components/form/forms.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { getContext, hasContext } from 'svelte';
-import type { SuperForm } from 'sveltekit-superforms';
-export const FORM_KEY = Symbol('form');
-
-export type RadioOptionType = {
- value: string | number;
- label: string;
-};
-
-export type FormContext> = {
- superform: SuperForm;
-};
-
-export function getFormContext>(): FormContext {
- if (!hasContext(FORM_KEY)) {
- throw new Error('FormField must be used within a Form component');
- }
- return getContext(FORM_KEY);
-}
diff --git a/apps/console/src/lib/components/form/index.ts b/apps/console/src/lib/components/form/index.ts
deleted file mode 100644
index 30acc4cf..00000000
--- a/apps/console/src/lib/components/form/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-export { default as Toggle } from './ToggleFB.svelte';
-export { default as Checkbox } from './CheckboxFB.svelte';
-export { default as DateInput } from './DateInput.svelte';
-export { default as DeleteForm } from './DeleteForm.svelte';
-export { default as ErrorMessage } from './ErrorMessage.svelte';
-export { default as FloatingTextInput } from './FloatingTextInput.svelte';
-export { default as Form } from './Form.svelte';
-export { default as FormAlerts } from './FormAlerts.svelte';
-export { default as Radio } from './RadioFB.svelte';
-export { default as Range } from './RangeFB.svelte';
-export { default as Select } from './Select.svelte';
-export { default as SelectDU } from './SelectDU.svelte';
-export { default as SelectFB } from './SelectFB.svelte';
-export { default as TagsInput } from './TagsInput.svelte';
-export { default as TextInput } from './TextInput.svelte';
-export { default as Textarea } from './Textarea.svelte';
diff --git a/apps/console/src/lib/components/form/inputChip/InputChip.svelte b/apps/console/src/lib/components/form/inputChip/InputChip.svelte
deleted file mode 100644
index 674024bf..00000000
--- a/apps/console/src/lib/components/form/inputChip/InputChip.svelte
+++ /dev/null
@@ -1,369 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- {#if chipValues.length}
-
- {#each chipValues as { id, val }, i (id)}
-
-
-
- {val}
-
-
-
- {/each}
-
- {/if}
-
diff --git a/apps/console/src/lib/components/form/inputChip/PrefersReducedMotion.ts b/apps/console/src/lib/components/form/inputChip/PrefersReducedMotion.ts
deleted file mode 100644
index d8f6b58a..00000000
--- a/apps/console/src/lib/components/form/inputChip/PrefersReducedMotion.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { readable } from 'svelte/store';
-import { BROWSER } from 'esm-env';
-
-/** Prefers reduced motion */
-const reducedMotionQuery = '(prefers-reduced-motion: reduce)';
-
-function prefersReducedMotion() {
- if (!BROWSER) return false;
- return window.matchMedia(reducedMotionQuery).matches;
-}
-
-/**
- * Indicates that the user has enabled reduced motion on their device.
- *
- * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
- */
-export const prefersReducedMotionStore = readable(prefersReducedMotion(), (set) => {
- if (BROWSER) {
- const setReducedMotion = (event: MediaQueryListEvent) => {
- set(event.matches);
- };
- const mediaQueryList = window.matchMedia(reducedMotionQuery);
- mediaQueryList.addEventListener('change', setReducedMotion);
-
- return () => {
- mediaQueryList.removeEventListener('change', setReducedMotion);
- };
- }
-});
diff --git a/apps/console/src/lib/components/form/inputChip/clipboard.ts b/apps/console/src/lib/components/form/inputChip/clipboard.ts
deleted file mode 100644
index 66493c76..00000000
--- a/apps/console/src/lib/components/form/inputChip/clipboard.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import type { ActionReturn } from 'svelte/action';
-
-// Action: Clipboard
-type ClipboardArgs = string | { element: string } | { input: string };
-type ClipboardAttributes = { 'on:copyComplete'?: () => void };
-export function clipboard(node: HTMLElement, args: ClipboardArgs): ActionReturn {
- if (!window.isSecureContext) {
- console.error(
- 'Clipboard action failed: app not running in secure context, see: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard'
- );
- return {};
- }
- const fireCopyCompleteEvent = () => {
- node.dispatchEvent(new CustomEvent('copyComplete'));
- };
- const onClick = () => {
- // Handle `data-clipboard` target based on object key
- if (typeof args === 'object') {
- // Element Inner HTML
- if ('element' in args) {
- const element = document.querySelector(`[data-clipboard="${args.element}"]`);
- if (!element) throw new Error(`Missing HTMLElement with an attribute of [data-clipboard="${args.element}"]`);
- copyToClipboard(element.innerHTML, 'text/html').then(fireCopyCompleteEvent);
- return;
- }
- // Form Input Value
- if ('input' in args) {
- const input = document.querySelector(`[data-clipboard="${args.input}"]`);
- if (!input) throw new Error(`Missing HTMLInputElement with an attribute of [data-clipboard="${args.input}"]`);
- copyToClipboard(input.value).then(fireCopyCompleteEvent);
- return;
- }
- }
- // Handle everything else.
- copyToClipboard(args).then(fireCopyCompleteEvent);
- };
- // Event Listener
- node.addEventListener('click', onClick);
- // Lifecycle
- return {
- update(newArgs: ClipboardArgs) {
- args = newArgs;
- },
- destroy() {
- node.removeEventListener('click', onClick);
- }
- };
-}
-
-// Shared copy method
-async function copyToClipboard(data: BlobPart, mimeType = 'text/plain') {
- if (navigator.clipboard.write) {
- await navigator.clipboard.write([
- new ClipboardItem({
- [mimeType]: new Blob([data], {
- type: mimeType
- }),
- ['text/plain']: new Blob([data], {
- type: 'text/plain'
- })
- })
- ]);
- } else {
- // fallback since .writeText has wider browser support
- await new Promise((resolve) => {
- resolve(navigator.clipboard.writeText(String(data)));
- });
- }
-}
diff --git a/apps/console/src/lib/components/form/inputChip/focusTrap.ts b/apps/console/src/lib/components/form/inputChip/focusTrap.ts
deleted file mode 100644
index 31eb182f..00000000
--- a/apps/console/src/lib/components/form/inputChip/focusTrap.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-// Action: Focus Trap
-export function focusTrap(node: HTMLElement, enabled: boolean) {
- const elemWhitelist =
- 'a[href]:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), textarea:not([tabindex="-1"]), select:not([tabindex="-1"]), details:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])';
- let elemFirst: HTMLElement;
- let elemLast: HTMLElement;
-
- // When the first element is selected, shift+tab pressed, jump to the last selectable item.
- function onFirstElemKeydown(e: KeyboardEvent): void {
- if (e.shiftKey && e.code === 'Tab') {
- e.preventDefault();
- elemLast.focus();
- }
- }
-
- // When the last item selected, tab pressed, jump to the first selectable item.
- function onLastElemKeydown(e: KeyboardEvent): void {
- if (!e.shiftKey && e.code === 'Tab') {
- e.preventDefault();
- elemFirst.focus();
- }
- }
-
- // Sort focusable elements by tabindex, positive first, then 0
- const sortByTabIndex = (focusableElems: HTMLElement[]): HTMLElement[] => {
- return focusableElems
- .filter((elem) => elem.tabIndex >= 0)
- .sort((a, b) => {
- if (a.tabIndex === 0 && b.tabIndex > 0)
- return 1; // Move 0 to end of array
- else if (a.tabIndex > 0 && b.tabIndex === 0)
- return -1; // Move 0 to end of array
- else return a.tabIndex - b.tabIndex; // Sort non-zero values in ascending order
- });
- };
-
- type FocusindexElement = HTMLElement & { dataset: { focusindex: string } };
-
- // Get element with smallest focusindex value, or first focusable element
- const getFocusTrapTarget = (elemFirst: HTMLElement) => {
- // Get elements with data-focusindex attribute
- const focusindexElements = [...node.querySelectorAll('[data-focusindex]')];
- if (!focusindexElements || focusindexElements.length === 0) return elemFirst;
- // return smallest focusindex element or elemFirst
- return (
- focusindexElements.sort((a, b) => {
- return +a.dataset.focusindex - +b.dataset.focusindex;
- })[0] || elemFirst
- );
- };
-
- const onScanElements = (fromObserver: boolean) => {
- if (enabled === false) return;
- // Gather all focusable elements, sorted according to tabindex
- const focusableElems: HTMLElement[] = sortByTabIndex(Array.from(node.querySelectorAll(elemWhitelist)));
- if (focusableElems.length) {
- // Set first/last focusable elements
- elemFirst = focusableElems[0];
- elemLast = focusableElems[focusableElems.length - 1];
- // Auto-focus focusTrapTarget or first focusable element only when not called from observer
- if (!fromObserver) getFocusTrapTarget(elemFirst).focus();
- // Listen for keydown on first & last element
- elemFirst.addEventListener('keydown', onFirstElemKeydown);
- elemLast.addEventListener('keydown', onLastElemKeydown);
- }
- };
- onScanElements(false);
-
- function onCleanUp(): void {
- if (elemFirst) elemFirst.removeEventListener('keydown', onFirstElemKeydown);
- if (elemLast) elemLast.removeEventListener('keydown', onLastElemKeydown);
- }
-
- // When children of node are changed (added or removed)
- const onObservationChange = (mutationRecords: MutationRecord[], observer: MutationObserver) => {
- if (mutationRecords.length) {
- onCleanUp();
- onScanElements(true);
- }
- return observer;
- };
- const observer = new MutationObserver(onObservationChange);
- observer.observe(node, { childList: true, subtree: true });
-
- // Lifecycle
- return {
- update(newArgs: boolean) {
- enabled = newArgs;
- newArgs ? onScanElements(false) : onCleanUp();
- },
- destroy() {
- onCleanUp();
- observer.disconnect();
- }
- };
-}
diff --git a/apps/console/src/lib/components/form/inputChip/index.ts b/apps/console/src/lib/components/form/inputChip/index.ts
deleted file mode 100644
index f933c24e..00000000
--- a/apps/console/src/lib/components/form/inputChip/index.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// This file defines the short path imports for the package (ex: @skeletonlabs/skeleton/*)
-
-// Types ---
-export type { Transition, TransitionParams } from './transitions.js';
-export type { CssClasses, SvelteEvent } from './types.js';
-
-// Prefers-reduced-motion
-export { prefersReducedMotionStore } from './PrefersReducedMotion.js';
-
-// Svelte Components
-export { default as InputChip } from './InputChip.svelte';
diff --git a/apps/console/src/lib/components/form/inputChip/transitions.ts b/apps/console/src/lib/components/form/inputChip/transitions.ts
deleted file mode 100644
index d98b94a5..00000000
--- a/apps/console/src/lib/components/form/inputChip/transitions.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import type { TransitionConfig } from 'svelte/transition';
-
-// Transitions ---
-export function dynamicTransition(node: Element, dynParams: DynamicTransitionParams): TransitionConfig {
- const { transition, params, enabled } = dynParams;
-
- if (enabled) return transition(node, params);
-
- // it's better to just set the `duration` to 0 to prevent flickering
- if ('duration' in params) return transition(node, { duration: 0 });
-
- // if the transition doesn't provide a `duration` prop, then we'll just return this as a last resort
- return { duration: 0 };
-}
-
-type DynamicTransitionParams = {
- transition: T;
- params: TransitionParams;
- enabled: boolean;
-};
-
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export type Transition = (node: Element, params?: any) => TransitionConfig;
-export type TransitionParams = Parameters[1];
diff --git a/apps/console/src/lib/components/form/inputChip/types.ts b/apps/console/src/lib/components/form/inputChip/types.ts
deleted file mode 100644
index 79e4e625..00000000
--- a/apps/console/src/lib/components/form/inputChip/types.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * This type alias is to identify CSS classes within component props,
- * which enables Tailwind IntelliSense
- */
-export type CssClasses = string;
-
-export type SvelteEvent = E & { currentTarget: EventTarget & T };
diff --git a/apps/console/src/lib/components/toast/README.md b/apps/console/src/lib/components/toast/README.md
deleted file mode 100644
index 5d136bde..00000000
--- a/apps/console/src/lib/components/toast/README.md
+++ /dev/null
@@ -1,109 +0,0 @@
-# Toast
-
-## Usage
-
-1. Add `` component somewhere in your layout component (e.g. App.svelte or \_layout.svelte, etc).
-
- ```svelte
-
-
-
-
-
- ```
-
-2. Use `addToast()` method to emit notifications in a svelte component or in `ts` file.
-
- ```svelte
-
-
-
- Dashboard/test
-
-
-
- Settings
-
-
-
-
- ```
-
-## Configuration
-
-```html
-
-
-
-```
-
-| Property Name | Property Type | Property Default |
-| :------------ | :---------------------------------------------------------------------------------------------------------- | :--------------- |
-| placement | 'top-left' \| 'top-center' \| 'top-right' \| 'center' \| 'bottom-left' \| 'bottom-center' \| 'bottom-right' | 'center' |
-
-```svelte
-
-
-```
-
-| Property Name | Property Type | Property Default |
-| :------------ | :------------------------------------------ | :--------------- |
-| message | string | |
-| type | 'Error' \| 'Warning' \| 'Success' \| 'Info' | Info |
-| dismissible | boolean | true |
-| duration | number | 9000 |
diff --git a/apps/console/src/lib/components/toast/Toasts.svelte b/apps/console/src/lib/components/toast/Toasts.svelte
deleted file mode 100644
index d902364b..00000000
--- a/apps/console/src/lib/components/toast/Toasts.svelte
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-{#if $toasts}
-
- {#each $toasts as toast (toast.id)}
-
-
-
- {#if toast.type === ToastLevel.Success}
-
- {:else if toast.type === ToastLevel.Error}
-
- {:else if toast.type === ToastLevel.Warning}
-
- {:else}
-
- {/if}
-
-
- {toast.message}
-
- {#if toast.dismissible}
-
dismissToast(toast.id)} />
- {/if}
-
-
- {/each}
-
-{/if}
diff --git a/apps/console/src/lib/components/toast/index.ts b/apps/console/src/lib/components/toast/index.ts
deleted file mode 100644
index afdc77cc..00000000
--- a/apps/console/src/lib/components/toast/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { addToast, ToastLevel } from './store';
-export type { Toast } from './store';
-export { default as Toasts } from './Toasts.svelte';
diff --git a/apps/console/src/lib/components/toast/store.ts b/apps/console/src/lib/components/toast/store.ts
deleted file mode 100644
index 144ef6f5..00000000
--- a/apps/console/src/lib/components/toast/store.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { writable } from 'svelte/store';
-
-interface ToastModel {
- id: string;
- message: string;
- type: ToastLevel;
- duration: number;
- dismissible: boolean;
-}
-
-export interface Toast {
- message: string;
- type?: ToastLevel;
- duration?: number;
- dismissible?: boolean;
-}
-
-export enum ToastLevel {
- Info = 'blue',
- Success = 'green',
- Warning = 'indigo',
- Error = 'red'
-}
-
-export const toasts = writable([]);
-
-// Setup some sensible defaults for a toast.
-const defaults = {
- type: ToastLevel.Info,
- dismissible: true,
- duration: 9000
-};
-
-export const dismissToast = (id: string) => {
- toasts.update((all) => all.filter((t) => t.id !== id));
-};
-
-export const addToast = (toast: Toast) => {
- // Create a unique ID so we can easily find/remove it
- // if it is dismissible/has a timeout.
- const id = crypto.randomUUID();
-
- // Push the toast to the top of the list of toasts
- const t = { ...defaults, ...toast, id };
- toasts.update((all) => [t, ...all]);
-
- // If toast is dismissible, dismiss it after "duration" amount of time.
- if (t.duration) setTimeout(() => dismissToast(id), t.duration);
-};
diff --git a/apps/console/src/lib/models/enums/index copy.ts b/apps/console/src/lib/models/enums/index copy.ts
deleted file mode 100644
index d3aac502..00000000
--- a/apps/console/src/lib/models/enums/index copy.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-export const limits = [
- { value: 5, name: '5' },
- { value: 10, name: '10' },
- { value: 20, name: '20' },
- { value: 50, name: '50' },
- { value: 100, name: '100' }
-];
-
-export const rows = [
- { value: 5, name: '5' },
- { value: 10, name: '10' },
- { value: 20, name: '20' },
- { value: 50, name: '50' },
- { value: 100, name: '100' }
-];
-
-export const subjectTypeOptions = [
- { value: '', name: 'All' },
- { value: 'user', name: 'User' },
- { value: 'group', name: 'Group' },
- { value: 'service_account', name: 'Service' },
- { value: 'device', name: 'Device' },
- { value: 'device_pool', name: 'Device Pool' }
-];
-
-export const subjectTypeOptions2 = [
- {
- value: 'user',
- label: 'User'
- },
- {
- value: 'group',
- label: 'Group'
- },
- {
- value: 'device',
- label: 'Device'
- },
- {
- value: 'device_pool',
- label: 'Device Pool'
- }
-];
-
-export const protocols = [
- { value: 'Any', name: 'Any' },
- { value: 'IP', name: 'IP' },
- { value: 'ICMP', name: 'ICMP' },
- { value: 'IGMP', name: 'IGMP' },
- { value: 'TCP', name: 'TCP' },
- { value: 'UDP', name: 'UDP' },
- { value: 'IPV6', name: 'IPV6' },
- { value: 'ICMPV6', name: 'ICMPV6' },
- { value: 'RM', name: 'RM' }
-];
-
-export const actionOptions = [
- {
- value: 'permit',
- label: 'Allow'
- },
- {
- value: 'block',
- label: 'Deny'
- }
-];
-
-export const directionOptions = [
- {
- value: 'egress',
- label: 'Egress' // Outbound
- },
- {
- value: 'ingress',
- label: 'Ingress' // Inbound
- }
-];
diff --git a/apps/console/src/lib/models/enums/index.ts b/apps/console/src/lib/models/enums/index.ts
deleted file mode 100644
index c771c176..00000000
--- a/apps/console/src/lib/models/enums/index.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-export const limits = [
- { value: 5, name: '5' },
- { value: 10, name: '10' },
- { value: 20, name: '20' },
- { value: 50, name: '50' },
- { value: 100, name: '100' }
-];
-
-export const rows = [
- { value: 5, name: '5' },
- { value: 10, name: '10' },
- { value: 20, name: '20' },
- { value: 50, name: '50' },
- { value: 100, name: '100' }
-];
-
-export const subjectTypeOptions = [
- { value: '', name: 'All' },
- { value: 'user', name: 'User' },
- { value: 'group', name: 'Group' },
- { value: 'service_account', name: 'Service' },
- { value: 'device', name: 'Device' },
- { value: 'device_pool', name: 'Device Pool' }
-];
-
-export enum Flow_Log_Events_Tag_Keys_Enum {
- ALL = 'all',
- USER = 'user-id',
- DEVICE = 'hostname',
- APPLICATION = 'app'
-}
-
-export const subjectTypeOptions2 = [
- {
- value: 'user',
- label: 'User'
- },
- {
- value: 'group',
- label: 'Group'
- },
- {
- value: 'device',
- label: 'Device'
- },
- {
- value: 'device_pool',
- label: 'Device Pool'
- }
-];
-
-export const protocols = [
- { value: 'Any', name: 'Any' },
- { value: 'IP', name: 'IP' },
- { value: 'ICMP', name: 'ICMP' },
- { value: 'IGMP', name: 'IGMP' },
- { value: 'TCP', name: 'TCP' },
- { value: 'UDP', name: 'UDP' },
- { value: 'IPV6', name: 'IPV6' },
- { value: 'ICMPV6', name: 'ICMPV6' },
- { value: 'RM', name: 'RM' }
-];
-
-export const actionOptions = [
- {
- value: 'permit',
- label: 'Allow'
- },
- {
- value: 'block',
- label: 'Deny'
- }
-];
-
-export const directionOptions = [
- {
- value: 'egress',
- label: 'Egress' // Outbound
- },
- {
- value: 'ingress',
- label: 'Ingress' // Inbound
- }
-];
diff --git a/apps/console/src/lib/schema/rule.ts b/apps/console/src/lib/schema/rule.ts
index d60f3b80..4816bc61 100644
--- a/apps/console/src/lib/schema/rule.ts
+++ b/apps/console/src/lib/schema/rule.ts
@@ -1,39 +1,63 @@
-import { z } from 'zod';
import { action_enum, direction_enum, protocol_enum } from '$houdini';
+import { z } from 'zod';
+/**
+ * Rule Schema
+ */
export const ruleSchema = z.object({
- id: z.string().uuid(),
- displayName: z.string().min(1).max(255),
- description: z.string().optional(),
- tags: z.array(z.string()).optional(),
- annotations: z.string().optional(),
- source: z.string().optional(),
- sourcePort: z.string().optional(),
- destination: z.string().optional(),
- destinationPort: z.string().optional(),
+ id: z.string().trim().uuid(),
+ displayName: z.string().trim().min(4).max(256),
+ description: z.string().trim().max(256).nullish(),
+ tags: z.string().trim().min(2).array().max(5).default([]),
+ annotations: z.string().trim().nullish(), // TODO: validate map string
+ source: z.string().ip().nullish(),
+ sourcePort: z.string().trim().nullish(),
+ destination: z.string().ip().nullish(),
+ destinationPort: z.string().trim().nullish(),
protocol: z.nativeEnum(protocol_enum).default(protocol_enum.Any),
action: z.nativeEnum(action_enum).default(action_enum.block),
direction: z.nativeEnum(direction_enum).default(direction_enum.egress),
- appId: z.string().optional(),
- throttleRate: z.number().min(0).max(100).optional(),
- weight: z.number().min(0).max(1000).optional(),
- shared: z.boolean().default(false),
- updatedAt: z.date().optional(),
+ appId: z.string().trim().nullish(),
+ throttleRate: z.coerce.number().min(0).max(100).optional().default(80),
+ weight: z.coerce.number().min(0).max(1000).optional().default(1000),
+ shared: z.boolean().optional().default(false),
});
-export const createRuleSchema = ruleSchema.omit({ id: true, updatedAt: true });
-export const updateRuleSchema = ruleSchema.partial().omit({ id: true });
+export type RuleSchema = typeof ruleSchema;
+export type Rule = z.infer;
+/**
+ * Search Rule Schema
+ */
export const ruleSearchSchema = z.object({
- displayName: z.string().optional(),
- limit: z.number().int().min(1).max(100).default(50),
+ limit: z.number().int().min(5).max(100).default(10),
offset: z.number().int().min(0).default(0),
+ displayName: z.string().trim().optional(),
+ tags: z.array(z.string()).optional(),
});
-
-export type Rule = z.infer;
-export type CreateRule = z.infer;
-export type UpdateRule = z.infer;
+export type RuleSearchSchema = typeof ruleSearchSchema;
export type RuleSearch = z.infer;
+/**
+ * Create Rule Schema
+ */
+export const createRuleSchema = ruleSchema.omit({ id: true });
+
+export type CreateRuleSchema = typeof createRuleSchema;
+export type CreateRule = z.infer;
export const createRuleKeys = createRuleSchema.keyof().Enum;
+
+/**
+ * Update Rule Schema
+ */
+export const updateRuleSchema = ruleSchema.partial().omit({ id: true }).extend({
+ originalShared: ruleSchema.shape.shared,
+});
+
+export type UpdateRuleSchema = typeof updateRuleSchema;
+export type UpdateRule = z.infer;
export const updateRuleKeys = updateRuleSchema.keyof().Enum;
+
+/**
+ * Refine functions can be added here if needed
+ */
diff --git a/apps/console/src/lib/utils/logger.ts b/apps/console/src/lib/utils/logger.ts
deleted file mode 100644
index 1fb1094b..00000000
--- a/apps/console/src/lib/utils/logger.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-export enum LogLevel {
- off = 0,
- Debug,
- Error,
- Warning,
- Info
-}
-
-export class Logger {
- static level = LogLevel.Debug;
-
- private source: string;
-
- /**
- * Enables production mode.
- * Sets logging level to LogLevel.Warning.
- */
- static enableProductionMode() {
- Logger.level = LogLevel.Warning;
- }
-
- constructor(component: string) {
- this.source = component;
- }
-
- public debug(...data: unknown[]): void {
- this.log(console.info, LogLevel.Debug, data);
- }
-
- public info(...data: unknown[]): void {
- this.log(console.info, LogLevel.Info, data);
- }
-
- public warn(...data: unknown[]): void {
- this.log(console.warn, LogLevel.Warning, data);
- }
-
- public error(...data: unknown[]): void {
- this.log(console.error, LogLevel.Error, data);
- }
-
- private log = (fun: () => void, level: LogLevel, objects: unknown[]): void => {
- if (level >= Logger.level) {
- const log = this.source ? [`[${this.source}]`].concat(objects as string[]) : objects;
- fun.apply(console, log);
- }
- };
-}
diff --git a/apps/console/src/routes/(app)/rules/create/+page.server.ts b/apps/console/src/routes/(app)/rules/create/+page.server.ts
index a25c1966..e69de29b 100644
--- a/apps/console/src/routes/(app)/rules/create/+page.server.ts
+++ b/apps/console/src/routes/(app)/rules/create/+page.server.ts
@@ -1,68 +0,0 @@
-import { CreateRuleStore, type rules_insert_input } from '$houdini';
-import { ToastLevel } from '$lib/components/toast';
-import { createRuleSchema as schema } from '$lib/schema/rule';
-import { Logger, cleanClone } from '$lib/utils';
-import { fail } from '@sveltejs/kit';
-import type { GraphQLError } from 'graphql';
-import { redirect } from 'sveltekit-flash-message/server';
-import { setError, setMessage, superValidate } from 'sveltekit-superforms';
-import { zod } from 'sveltekit-superforms/adapters';
-
-const log = new Logger('rule.create.server');
-
-const createRuleStore = new CreateRuleStore();
-export const actions = {
- default: async (event) => {
- const { request, locals } = event;
- const session = await locals.auth();
- if (session?.user == undefined) {
- throw redirect(307, '/auth/signin?callbackUrl=/dashboard/rules/create');
- }
-
- const form = await superValidate(request, zod(schema));
- log.debug({ form });
-
- // superform validation
- if (!form.valid) return fail(400, { form });
-
- log.debug('before cleanClone with strip:', form.data);
- const dataCopy = cleanClone(form.data, { empty: 'strip' });
- log.debug('after cleanClone with strip:', dataCopy);
-
- const payload: rules_insert_input = {
- ...dataCopy,
- shared: true,
- ...(dataCopy.tags && { tags: `{${dataCopy.tags}}` }),
- ...(dataCopy.throttleRate >= 0 ? { throttleRate: `${dataCopy.throttleRate}` } : { throttleRate: '0' })
- };
- const variables = { data: payload };
- log.debug('CREATE action variables:', variables);
- const { errors, data } = await createRuleStore.mutate(variables, {
- metadata: { logResult: true },
- event
- });
- if (errors) {
- errors.forEach((error) => {
- log.error('create rule api error', error);
- // NOTE: you can add multiple errors, send all along with a message
- if (error.message.includes('Uniqueness violation')) {
- setError(form, 'displayName', 'Display Name already taken');
- } else {
- setError(form, '', (error as GraphQLError).message);
- }
- });
- return setMessage(form, 'Create rule failed');
- }
-
- const result = data?.insert_rules_one;
- if (!result) return setMessage(form, 'Create rule failed: responce empty', { status: 404 });
-
- const message = {
- message: `Rule: ${result.displayName} created`,
- dismissible: true,
- duration: 10000,
- type: ToastLevel.Success
- } as const;
- throw redirect(302, '/dashboard/rules', message, event);
- }
-};
diff --git a/apps/console/src/routes/(app)/rules/create/+page.svelte b/apps/console/src/routes/(app)/rules/create/+page.svelte
index 0e7aa33e..aa7da02b 100644
--- a/apps/console/src/routes/(app)/rules/create/+page.svelte
+++ b/apps/console/src/routes/(app)/rules/create/+page.svelte
@@ -1,183 +1,420 @@
- Rules | Create
-
+ Rules
+
-
- Home
- Rules
- Create Rule
-
-
-Create Rule
-
-
-
-{#if dev}
-
-
-
-
-
-
-
-
-
-
-
-{/if}
+
diff --git a/apps/console/src/routes/(app)/rules/create/+page.ts b/apps/console/src/routes/(app)/rules/create/+page.ts
index 17d5c4d0..e69de29b 100644
--- a/apps/console/src/routes/(app)/rules/create/+page.ts
+++ b/apps/console/src/routes/(app)/rules/create/+page.ts
@@ -1,11 +0,0 @@
-import { superValidate } from 'sveltekit-superforms/client';
-import { createRuleSchema as schema } from '$lib/schema/rule';
-import { zod } from 'sveltekit-superforms/adapters';
-
-export const load = async () => {
- // Client API:
- const form = await superValidate(zod(schema));
-
- // Always return { form } in load and form actions.
- return { schema, form };
-};