Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: Basic structured data support
  • Loading branch information
cdata authored and bfollington committed Jun 6, 2024
commit 571df257570dc737b19d2c062670d3b1917303ee
4 changes: 2 additions & 2 deletions typescript/common/data/wit/data.wit
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface types {
deref: func() -> result<option<value>, %string>;
}

resource map {
resource dictionary {
get: func(key: %string) -> option<reference>;
}

Expand All @@ -24,7 +24,7 @@ interface types {
boolean(boolean),
buffer(buffer),
array(array),
map(map)
dictionary(dictionary)
}
}

Expand Down
4 changes: 2 additions & 2 deletions typescript/common/io/wit/deps.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[data]
path = "../../data/wit"
sha256 = "4ebf9b97080ec72fc805dd861879c18727cffd64110cabe4176f73c4013b8c13"
sha512 = "9ab5472c3141fae10193c7407207f9b010757bba3219e73cc34179067cf9be4a2d68f3ddfc87491a847051b87ef9dae8c69e2fdfc8553a9ab4e6cd0d22cdaa41"
sha256 = "e092c9d7a74aaab5a9a99dcf43ae00f3bcf70d78cb5841124dae526698888815"
sha512 = "e55af0160d09492d61567710cf0fa0c0ae032931cc9f0cc4b44a7e1f4706ca879034790d6c9f393be31711530a4751222767708c16701cf076d2053eec27d212"
4 changes: 2 additions & 2 deletions typescript/common/module/wit/deps.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[data]
path = "../../data/wit"
sha256 = "4ebf9b97080ec72fc805dd861879c18727cffd64110cabe4176f73c4013b8c13"
sha512 = "9ab5472c3141fae10193c7407207f9b010757bba3219e73cc34179067cf9be4a2d68f3ddfc87491a847051b87ef9dae8c69e2fdfc8553a9ab4e6cd0d22cdaa41"
sha256 = "e092c9d7a74aaab5a9a99dcf43ae00f3bcf70d78cb5841124dae526698888815"
sha512 = "e55af0160d09492d61567710cf0fa0c0ae032931cc9f0cc4b44a7e1f4706ca879034790d6c9f393be31711530a4751222767708c16701cf076d2053eec27d212"

[io]
path = "../../io/wit"
Expand Down
28 changes: 28 additions & 0 deletions typescript/common/runtime/src/dictionary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type {
Dictionary as CommonDictionary,
Value,
} from '@commontools/data/interfaces/common-data-types.js';
import { IO as CommonIO } from './io.js';
import { infer } from './infer.js';
import { Reference } from './reference.js';

export class Dictionary implements CommonDictionary, CommonIO {
#inner: any;

constructor(inner: any) {
this.#inner = inner;
}

read(key: string): Value | undefined {
console.log(`Reading '${key}' from`, this.#inner);
return infer(this.#inner[key]);
}

write(_key: string, _value: Value): void {
throw new Error('Write into dictionary is not supported');
}

get(key: string): Reference | undefined {
return new Reference(this, key);
}
}
77 changes: 11 additions & 66 deletions typescript/common/runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,24 @@ import { Runtime as UsubaRuntime } from '@commontools/usuba-rt';
import { wit as commonDataWit } from '@commontools/data';
import { wit as commonIoWit } from '@commontools/io';
import { wit as commonModuleWit } from '@commontools/module';
import type {
Value,
Reference as CommonReference,
} from '@commontools/data/interfaces/common-data-types.js';
import type { Value } from '@commontools/data/interfaces/common-data-types.js';
import type { IO } from './io.js';
import { Reference } from './reference.js';
import { Dictionary } from './dictionary.js';

export type * from '@commontools/data/interfaces/common-data-types.js';
export type { Value } from '@commontools/data/interfaces/common-data-types.js';

export type ContentType = 'text/javascript';
export * from './io.js';
export * from './dictionary.js';
export * from './reference.js';
export * from './infer.js';

export interface IO {
read: (key: string) => Value | undefined;
write: (key: string, value: Value) => void;
}
export type ContentType = 'text/javascript';

export interface Module {
run: () => void;
}

/**
* Attempts to infer a `Value` from any given JS value. See data.wit for a
* reference of all possible variants of `Value`. Inference has marginal cost;
* it is more efficient to avoid it if you can.
*
* NOTE: Currently "array" and "map" `Value` types aren't really going to work.
* Stick to "string", "number", "boolean" and "buffer".
*/
export const infer = (value: any): Value | undefined => {
const val = value;
let tag: string = typeof value;

switch (tag) {
case 'string':
case 'boolean':
case 'number':
return {
tag,
val,
};
default: {
if (Array.isArray(val)) {
return {
tag: 'array',
val,
};
}

if (val instanceof Uint8Array) {
return {
tag: 'buffer',
val,
};
}
}
}

return undefined;
};

class Reference implements CommonReference {
#io;
#key;

constructor(io: IO, key: string) {
// TODO: Validate attempt to read (aka attempt to create a Reference) here
this.#io = io;
this.#key = key;
}

deref(): Value | undefined {
return this.#io.read(this.#key);
}
}

export class Runtime {
#inner = new UsubaRuntime([commonDataWit, commonIoWit]);

Expand All @@ -94,7 +39,7 @@ export class Runtime {
const { module } = await blueprint.instantiate({
'common:data/types': {
Reference,
Map,
Dictionary,
},
'common:io/state': {
read(name: string) {
Expand Down
50 changes: 50 additions & 0 deletions typescript/common/runtime/src/infer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Value } from '@commontools/data/interfaces/common-data-types.js';
import { Dictionary } from './dictionary.js';

/**
* Attempts to infer a `Value` from any given JS value. See data.wit for a
* reference of all possible variants of `Value`. Inference has marginal cost;
* it is more efficient to avoid it if you can.
*
* NOTE: Currently "array" `Value` types aren't really going to work.
* Stick to "string", "number", "boolean", "buffer" and "dictionary".
*/
export const infer = (value: any): Value | undefined => {
const val = value;
let tag: string = typeof value;

switch (tag) {
case 'string':
case 'boolean':
case 'number':
return {
tag,
val,
};
case 'object': {
if (Array.isArray(val)) {
return {
tag: 'array',
val,
};
}

if (val instanceof Uint8Array) {
return {
tag: 'buffer',
val,
};
}

// NOTE: `typeof null == 'object'`
if (val) {
return {
tag: 'dictionary',
val: new Dictionary(val),
};
}
}
}

return undefined;
};
6 changes: 6 additions & 0 deletions typescript/common/runtime/src/io.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Value } from '@commontools/data/interfaces/common-data-types.js';

export interface IO {
read: (key: string) => Value | undefined;
write: (key: string, value: Value) => void;
}
20 changes: 20 additions & 0 deletions typescript/common/runtime/src/reference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type {
Value,
Reference as CommonReference,
} from '@commontools/data/interfaces/common-data-types.js';
import { IO } from './io.js';

export class Reference implements CommonReference {
#io;
#key;

constructor(io: IO, key: string) {
// TODO: Validate attempt to read (aka attempt to create a Reference) here
this.#io = io;
this.#key = key;
}

deref(): Value | undefined {
return this.#io.read(this.#key);
}
}
24 changes: 22 additions & 2 deletions typescript/packages/runtime-demo/src/demo6.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { IO, Runtime, Value, infer } from '@commontools/runtime';
import { IO, Runtime, Dictionary, Value, infer } from '@commontools/runtime';

const EXAMPLE_MODULE_JS = `
import { read, write } from 'common:io/state@0.0.1';

export class Body {
run() {
console.log('Running!');

const foo = read('foo');
console.log('Reference:', foo);
const value = foo?.deref();

console.log('Value:', value);

const bar = read('bar');
const dict = bar?.deref()?.val;
const dictValue = dict.get('baz');

console.log('Dictionary value:', dictValue.deref()?.val);
}
}

Expand All @@ -22,13 +29,26 @@ export const module = {
}
};`;

const bar = new Dictionary({
baz: 'quux',
});

class LocalStorageIO implements IO {
reset() {
localStorage.clear();
}

read(key: string): Value | undefined {
if (key == 'bar') {
console.log(`Reading special key '${bar}'...`);
return {
tag: 'dictionary',
val: bar,
};
}

console.log(`Reading '${key}' from local storage`);

let rawValue = localStorage.getItem(key);
return infer(rawValue);
}
Expand Down