summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2025-07-09 23:29:06 -0400
committerOwen Jacobson <owen@grimoire.ca>2025-08-21 19:42:09 -0400
commita5326a67e37d9f1aee740f7b3d46345f3bcda419 (patch)
tree18a1eff075e12c60e2bc2400703b9ad2cc67e7d4
parent4624f4dbebf5dd1ed4dc5168573537459b9a115e (diff)
Factor data-to-JSON-string construction out of stitches.
This is a recurring and nameable operation; let's give it a name before we use it further.
-rw-r--r--ui/lib/swatch/derive.js6
-rw-r--r--ui/lib/swatch/json.js7
-rw-r--r--ui/lib/swatch/json.test.js36
-rw-r--r--ui/lib/swatch/setup.js0
-rw-r--r--ui/routes/(swatch)/.swatch/ConversationList/+page.svelte32
-rw-r--r--ui/routes/(swatch)/.swatch/Invites/+page.svelte8
-rw-r--r--ui/routes/(swatch)/.swatch/swatch/EventLog/+page.svelte13
7 files changed, 67 insertions, 35 deletions
diff --git a/ui/lib/swatch/derive.js b/ui/lib/swatch/derive.js
index 85547e8..22ceb13 100644
--- a/ui/lib/swatch/derive.js
+++ b/ui/lib/swatch/derive.js
@@ -5,16 +5,12 @@ function tryDerive(args, func, fallback) {
try {
return func(...args);
} catch (e) {
- console.debug('deriver threw exception', e, func, args);
return fallback;
}
}
// A "deriver" is a function that never raises; if the underlying function would raise, the
// corresponding deriver instead returns a fallback value (or `undefined`).
-export function makeDeriver(func, fallback) {
+export function makeDeriver(func, fallback = undefined) {
return (...args) => tryDerive(args, func, fallback);
}
-
-// Some widely-used derivers, for convenience.
-export const json = makeDeriver(JSON.parse);
diff --git a/ui/lib/swatch/json.js b/ui/lib/swatch/json.js
new file mode 100644
index 0000000..3ebbc0d
--- /dev/null
+++ b/ui/lib/swatch/json.js
@@ -0,0 +1,7 @@
+import { makeDeriver } from '$lib/swatch/derive.js';
+
+export const decode = makeDeriver(JSON.parse);
+
+export function encode(value) {
+ return JSON.stringify(value, /* replacer */ null, /* space */ 2);
+}
diff --git a/ui/lib/swatch/json.test.js b/ui/lib/swatch/json.test.js
new file mode 100644
index 0000000..e01017d
--- /dev/null
+++ b/ui/lib/swatch/json.test.js
@@ -0,0 +1,36 @@
+import * as json from './json.js';
+import { expect, describe, it } from 'vitest';
+
+describe('encode', async () => {
+ it('converts atoms', async () => {
+ expect(json.encode(0)).toStrictEqual('0');
+ expect(json.encode('hello')).toStrictEqual('"hello"');
+ expect(json.encode(null)).toStrictEqual('null');
+ expect(json.encode(true)).toStrictEqual('true');
+ });
+
+ it('converts lists with indentation', async () => {
+ expect(json.encode([])).toStrictEqual('[]');
+ expect(json.encode(['hello', 'world'])).toStrictEqual('[\n "hello",\n "world"\n]');
+ });
+
+ it('converts objects with indentation', async () => {
+ expect(json.encode({})).toStrictEqual('{}');
+ expect(json.encode({ field: 'value' })).toStrictEqual('{\n "field": "value"\n}');
+ });
+});
+
+describe('decode', async () => {
+ it('converts valid json values', async () => {
+ expect(json.decode('0')).toStrictEqual(0);
+ expect(json.decode('null')).toStrictEqual(null);
+ expect(json.decode('{}')).toStrictEqual({});
+ expect(json.decode('[1, 2, "3"]')).toStrictEqual([1, 2, '3']);
+ });
+
+ it('converts invalid json to `undefined`', async () => {
+ expect(json.decode('')).toBeUndefined();
+ expect(json.decode('{"foo":')).toBeUndefined();
+ expect(json.decode('arbitrary nonsense')).toBeUndefined();
+ });
+});
diff --git a/ui/lib/swatch/setup.js b/ui/lib/swatch/setup.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ui/lib/swatch/setup.js
diff --git a/ui/routes/(swatch)/.swatch/ConversationList/+page.svelte b/ui/routes/(swatch)/.swatch/ConversationList/+page.svelte
index ef0c8a9..b0bc97a 100644
--- a/ui/routes/(swatch)/.swatch/ConversationList/+page.svelte
+++ b/ui/routes/(swatch)/.swatch/ConversationList/+page.svelte
@@ -1,27 +1,23 @@
<script>
- import { json } from '$lib/swatch/derive.js';
+ import * as json from '$lib/swatch/json.js';
import ConversationList from '$lib/components/ConversationList.svelte';
let conversationsInput = $state(
- JSON.stringify(
- [
- {
- id: 'Czero',
- name: 'A long conversation',
- hasUnreads: false,
- },
- {
- id: 'Cone',
- name: 'A shorter conversation',
- hasUnreads: true,
- },
- ],
- /* replacer */ null,
- /* space */ 2,
- ),
+ json.encode([
+ {
+ id: 'Czero',
+ name: 'A long conversation',
+ hasUnreads: false,
+ },
+ {
+ id: 'Cone',
+ name: 'A shorter conversation',
+ hasUnreads: true,
+ },
+ ]),
);
- let conversations = $derived(json(conversationsInput));
+ let conversations = $derived(json.decode(conversationsInput));
let active = $state(null);
</script>
diff --git a/ui/routes/(swatch)/.swatch/Invites/+page.svelte b/ui/routes/(swatch)/.swatch/Invites/+page.svelte
index 8c24627..bc4bdb1 100644
--- a/ui/routes/(swatch)/.swatch/Invites/+page.svelte
+++ b/ui/routes/(swatch)/.swatch/Invites/+page.svelte
@@ -1,14 +1,12 @@
<script>
- import { json } from '$lib/swatch/derive.js';
+ import * as json from '$lib/swatch/json.js';
import EventCapture from '$lib/swatch/event-capture.svelte.js';
import EventLog from '$lib/components/swatch/EventLog.svelte';
import Invites from '$lib/components/Invites.svelte';
- let invitesInput = $state(
- JSON.stringify([{ id: 'Iaaaa' }, { id: 'Ibbbb' }], /* replacer */ null, /* space */ 2),
- );
- let invites = $derived(json(invitesInput));
+ let invitesInput = $state(json.encode([{ id: 'Iaaaa' }, { id: 'Ibbbb' }]));
+ let invites = $derived(json.decode(invitesInput));
let capture = $state(new EventCapture());
const createInvite = capture.on('createInvite');
diff --git a/ui/routes/(swatch)/.swatch/swatch/EventLog/+page.svelte b/ui/routes/(swatch)/.swatch/swatch/EventLog/+page.svelte
index a751ca3..eae0838 100644
--- a/ui/routes/(swatch)/.swatch/swatch/EventLog/+page.svelte
+++ b/ui/routes/(swatch)/.swatch/swatch/EventLog/+page.svelte
@@ -1,17 +1,16 @@
<script>
import EventCapture from '$lib/swatch/event-capture.svelte.js';
- import { json } from '$lib/swatch/derive.js';
+ import * as json from '$lib/swatch/json.js';
import EventLog from '$lib/components/swatch/EventLog.svelte';
let eventsInput = $state(
- JSON.stringify(
- [{ event: 'example', args: ['argument a', 'argument b'] }],
- /* replacer */ null,
- /* space */ 2,
- ),
+ json.encode([
+ { event: 'firstExample', args: ['argument a', 'argument b'] },
+ { event: 'secondExample', args: ['argument 1'] },
+ ]),
);
- let events = $derived(json(eventsInput));
+ let events = $derived(json.decode(eventsInput));
let capture = $state(new EventCapture());
const clear = capture.on('clear');