Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F10361284
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/component-arguments/component-argument.ts b/src/component-arguments/component-argument.ts
index 1627bb1..e8beed9 100644
--- a/src/component-arguments/component-argument.ts
+++ b/src/component-arguments/component-argument.ts
@@ -1,109 +1,89 @@
import { JDDContext, List } from "../index.js";
export type ExtractComponentArgumentValue<C> = C extends List<infer S>
? S[]
: C extends ComponentArgument<infer T>
? T
: never;
export type ExtractStructuredComponentArgumentsParsed<C> = {
[property in keyof C]: ExtractComponentArgumentValue<C[property]>;
};
export type ExtractStructuredComponentArgumentsStorage<C> = {
[property in keyof C]: ExtractStorage<C[property]>;
};
export type ExtractStructuredComponentArgumentsReceived<C> = {
[property in keyof C]: ExtractReceived<C[property]>;
};
-export type ExtractStructuredComponentArgumentsFormdata<C> = {
- [property in keyof C]: ExtractFormData<C[property]>;
-};
-
export type ExtractParsed<C> = C extends ComponentArgument<infer T> ? T : never;
export type ExtractStorage<C> = C extends ComponentArgument<infer _, infer T>
? T
: never;
export type ExtractReceived<C> = C extends ComponentArgument<
infer _,
infer __,
infer T
>
? T
: never;
-export type ExtractFormData<C> = C extends ComponentArgument<
- infer _,
- infer __,
- infer ___,
- infer T
->
- ? T
- : never;
export abstract class ComponentArgument<
ParsedType,
StorageType = ParsedType,
- ReceivedDataType = StorageType,
- FormDataType extends Record<string, unknown> | string =
- | Record<string, unknown>
- | string
+ ReceivedDataType = StorageType
> {
public example_values: ParsedType[] = [];
public parent_argument: ComponentArgument<unknown> | null = null;
abstract getTypeName(): string;
abstract getEmptyValue(): ParsedType;
abstract countWords(value: ParsedType): number;
abstract getSubArgument(
key: string,
value: ParsedType
): ComponentArgument<unknown> | null;
// Received - what we get on the server side when receiveing the request
// Parsed - the useful form of the value, ready for processing
// Storage - how it's represented when storing in a JSON form
// Formdata - how it's represented when it needs to be sent to the server
abstract receivedToParsed(
context: JDDContext,
value: ReceivedDataType
): Promise<ParsedType | ParsedType[]>;
abstract parsedToStorage(
context: JDDContext,
value: ParsedType
): Promise<StorageType>;
abstract storageToParsed(
context: JDDContext,
value: StorageType
): Promise<ParsedType>;
- abstract parsedToFormData(
- context: JDDContext,
- value: ParsedType
- ): Promise<FormDataType>;
-
getExampleValue(_context: JDDContext): Promise<ParsedType> | ParsedType {
return this.example_values[
Math.floor(this.example_values.length * Math.random())
];
}
setExampleValues(values: ParsedType[]): this {
this.example_values = values;
return this;
}
hasParent(type: string) {
let parent = this.parent_argument;
while (parent) {
if (parent.getTypeName() == type) {
return true;
}
parent = parent.parent_argument;
}
return false;
}
}
diff --git a/src/component-arguments/container-argument.ts b/src/component-arguments/container-argument.ts
index 120d784..7c26e88 100644
--- a/src/component-arguments/container-argument.ts
+++ b/src/component-arguments/container-argument.ts
@@ -1,60 +1,47 @@
import { JDDContext } from "../jdd-context.js";
import { ComponentArgument } from "./component-argument.js";
export abstract class ContainerArgument<
P,
S = P,
- R = P,
- F extends Record<string, unknown> | string =
- | Record<string, unknown>
- | string
-> extends ComponentArgument<P, S, R, F> {
+ R = P
+> extends ComponentArgument<P, S, R> {
abstract processAllSubarguments(
context: JDDContext,
values: unknown,
processing_function: (
argument: ComponentArgument<unknown>,
value: unknown
) => Promise<unknown>
): unknown;
async receivedToParsed(context: JDDContext, value: R): Promise<P> {
return this.processAllSubarguments(
context,
value,
(argument: ComponentArgument<unknown>, value: unknown) => {
return argument.receivedToParsed(context, value);
}
) as P;
}
async parsedToStorage(context: JDDContext, value: P): Promise<S> {
return this.processAllSubarguments(
context,
value,
(argument: ComponentArgument<unknown>, value: unknown) => {
return argument.parsedToStorage(context, value);
}
) as S;
}
async storageToParsed(context: JDDContext, value: S): Promise<P> {
return this.processAllSubarguments(
context,
value,
(argument: ComponentArgument<unknown>, value: unknown) => {
return argument.storageToParsed(context, value);
}
) as P;
}
-
- async parsedToFormData(context: JDDContext, value: P): Promise<F> {
- return this.processAllSubarguments(
- context,
- value,
- (argument: ComponentArgument<unknown>, value: unknown) => {
- return argument.parsedToFormData(context, value);
- }
- ) as F;
- }
}
diff --git a/src/component-arguments/file.ts b/src/component-arguments/file.ts
index 6193323..1555cc9 100644
--- a/src/component-arguments/file.ts
+++ b/src/component-arguments/file.ts
@@ -1,82 +1,81 @@
import { JDDContext } from "../index.js";
import { ComponentArgument } from "./component-argument.js";
import { FilePointer } from "@sealcode/file-manager";
type FileReceivedType = {
old: FilePointer | null;
new: FilePointer[] | FilePointer | null;
};
type FileFormDataType = {
old: string | null;
new?: unknown; // the browser will take care of setting a value to thag attribute via html input, we don't need to model this
};
export class File extends ComponentArgument<
FilePointer | null,
string | null,
- FileReceivedType,
- FileFormDataType
+ FileReceivedType
> {
constructor() {
super();
}
getTypeName() {
return "file";
}
getEmptyValue() {
return null;
}
getSubArgument() {
return null;
}
async getExampleValue(_context: JDDContext): Promise<FilePointer | null> {
return null;
}
countWords(): number {
return 0;
}
async receivedToParsed(_context: JDDContext, value: FileReceivedType) {
if (value.new && Array.isArray(value.new)) {
if (value.new.length == 0) {
return value.old || null;
} else if (value.new.length == 1) {
await value.new[0].save(true);
return value.new[0];
} else {
await Promise.all(value.new.map((f) => f.save(true)));
return value.new;
}
} else {
return value.old || null;
}
}
async parsedToStorage(_context: JDDContext, value: FilePointer | null) {
if (!value) {
return null;
}
await value.save(true);
return value.token;
}
async storageToParsed(
context: JDDContext,
value: string | null
): Promise<FilePointer | null> {
return value ? context.file_manager.fromToken(value) : null;
}
async parsedToFormData(
_context: JDDContext,
value: FilePointer | null
): Promise<FileFormDataType> {
return { old: value?.token || null };
}
}
diff --git a/src/component-arguments/list.test.ts b/src/component-arguments/list.test.ts
new file mode 100644
index 0000000..58d64a3
--- /dev/null
+++ b/src/component-arguments/list.test.ts
@@ -0,0 +1,8 @@
+import { Image } from "./image.js";
+import { List } from "./list.js";
+
+describe("list", () => {
+ it("allows for creating a list of images", () => {
+ new List(new Image());
+ });
+});
diff --git a/src/component-arguments/list.ts b/src/component-arguments/list.ts
index 669385a..becdcb9 100644
--- a/src/component-arguments/list.ts
+++ b/src/component-arguments/list.ts
@@ -1,91 +1,94 @@
import { is, predicates } from "@sealcode/ts-predicates";
import { JDDContext } from "../index.js";
import { MaybePromise } from "../utils/util-types.js";
-import { ComponentArgument, ExtractStorage } from "./component-argument.js";
+import { ComponentArgument, ExtractParsed } from "./component-argument.js";
import { ContainerArgument } from "./container-argument.js";
-export class List<T, TS = ExtractStorage<T>> extends ContainerArgument<
- Array<T>,
- Array<TS>
-> {
+export class List<
+ T extends ComponentArgument<unknown>
+> extends ContainerArgument<ExtractParsed<T>[], unknown, unknown> {
constructor(
- public item_type: ComponentArgument<T>,
+ public item_type: ComponentArgument<ExtractParsed<T>, unknown, unknown>,
public example_count: number | null = null
) {
super();
item_type.parent_argument = this;
}
getTypeName() {
return "list";
}
getEmptyValue() {
return [];
}
getSubArgument(key: string) {
if (isNaN(parseInt(key))) {
return null;
}
return this.item_type;
}
getExampleCount() {
if (this.example_count === null) {
return Math.floor(Math.random() * 5);
} else {
return this.example_count;
}
}
async getExampleValue(context: JDDContext) {
if (this.example_values.length) {
return super.getExampleValue(context);
} else {
const count = this.getExampleCount();
- const result = [] as Array<MaybePromise<T>>;
+ const result = [] as Array<MaybePromise<ExtractParsed<T>>>;
for (let i = 0; i < count; i++) {
- result.push(this.item_type.getExampleValue(context));
+ result.push(
+ this.item_type.getExampleValue(context) as Promise<
+ ExtractParsed<T>
+ >
+ );
}
- return (await Promise.all(result)) as Array<T>;
+ return await Promise.all(result);
}
}
- countWords(value: Array<T>): number {
+ countWords(value: Array<ExtractParsed<T>>): number {
return value.reduce(
(acc, item) => acc + this.item_type.countWords(item),
0
);
}
async processAllSubarguments(
_context: JDDContext,
values: unknown,
processing_function: (
argument: ComponentArgument<unknown>,
value: unknown
) => Promise<unknown>
): Promise<T[] | null> {
if (
!is(values, predicates.array(predicates.object)) &&
!is(values, predicates.object)
) {
throw new Error(`Not a list or object: ${values as string}`);
}
const values_array = Array.isArray(values)
? values
: Object.values(values);
let array_result = (await Promise.all(
values_array.map(async (value) => {
const result = await processing_function(this.item_type, value);
return result;
})
)) as Array<T | T[] | null>;
if (this.item_type.getTypeName() != "list") {
array_result = array_result.flat() as T[];
}
const result = array_result.filter((e) => e !== null) as T[];
return result;
}
}
diff --git a/src/component.ts b/src/component.ts
index fbc3803..868d138 100644
--- a/src/component.ts
+++ b/src/component.ts
@@ -1,165 +1,146 @@
import { FlatTemplatable } from "tempstream";
import {
ComponentArgument,
- ExtractStructuredComponentArgumentsFormdata,
ExtractStructuredComponentArgumentsParsed,
ExtractStructuredComponentArgumentsReceived,
ExtractStructuredComponentArgumentsStorage,
JDDContext,
} from "./index.js";
export interface ComponentConstructor<
A extends Record<string, ComponentArgument<unknown>> = Record<
string,
ComponentArgument<unknown>
>
> {
new (): Component<A>;
}
export type EarlyAsset = (
| { type: "script" | "style"; url: string; integrity?: string }
| { type: "script" | "style"; content: string }
) & { identity: string }; // identity key will be used for deduplication
export abstract class Component<
ArgumentsT extends Record<string, ComponentArgument<unknown>> = Record<
string,
ComponentArgument<unknown>
>
> {
abstract getArguments(): ArgumentsT;
abstract toHTML(
args: ExtractStructuredComponentArgumentsParsed<ArgumentsT>,
context: JDDContext
): FlatTemplatable;
async getEarlyAssets(
_args: ExtractStructuredComponentArgumentsParsed<ArgumentsT>,
_context: JDDContext
): Promise<EarlyAsset[]> {
return [];
}
countWords(args: Record<string, unknown>): number {
return Object.entries(args).reduce((acc, [arg_name, value]) => {
const arg = this.getArguments()[arg_name];
if (!arg) {
console.warn(
`Arguemnt ${arg_name} was not found in the component`
);
return acc + 0;
}
return acc + arg.countWords(value);
}, 0);
}
async getExampleValues(
context: JDDContext
): Promise<ExtractStructuredComponentArgumentsParsed<ArgumentsT>> {
return Object.fromEntries(
await Promise.all(
Object.entries(this.getArguments()).map(
async ([key, value]) => [
key,
await value.getExampleValue(context),
]
)
)
) as ExtractStructuredComponentArgumentsParsed<ArgumentsT>;
}
async convertReceivedValuesToParsed(
context: JDDContext,
values: ExtractStructuredComponentArgumentsReceived<ArgumentsT>
): Promise<ExtractStructuredComponentArgumentsParsed<ArgumentsT>> {
const args = this.getArguments();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Object.fromEntries(
await Promise.all(
Object.entries(values).map(async ([key, value]) => {
return [
key,
await args[key].receivedToParsed(context, value),
];
})
)
);
}
async convertParsedToStorage(
context: JDDContext,
values: ExtractStructuredComponentArgumentsParsed<ArgumentsT>
): Promise<ExtractStructuredComponentArgumentsStorage<ArgumentsT>> {
const args = this.getArguments();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Object.fromEntries(
await Promise.all(
Object.entries(values).map(async ([key, value]) => {
return [
key,
await args[key].parsedToStorage(context, value),
];
})
)
);
}
async convertStorageToParsed(
context: JDDContext,
values: ExtractStructuredComponentArgumentsStorage<ArgumentsT>
): Promise<ExtractStructuredComponentArgumentsParsed<ArgumentsT>> {
const args = this.getArguments();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Object.fromEntries(
await Promise.all(
Object.entries(values).map(async ([key, value]) => {
return [
key,
await args[key].storageToParsed(context, value),
];
})
)
);
}
- async convertParsedToFormdata(
- context: JDDContext,
- values: ExtractStructuredComponentArgumentsParsed<ArgumentsT>
- ): Promise<ExtractStructuredComponentArgumentsFormdata<ArgumentsT>> {
- const args = this.getArguments();
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
- return Object.fromEntries(
- await Promise.all(
- Object.entries(values).map(async ([key, value]) => {
- return [
- key,
- await args[key].parsedToFormData(context, value),
- ];
- })
- )
- );
- }
-
getArgumentAtPath(
argument_path: string[],
values: ExtractStructuredComponentArgumentsParsed<ArgumentsT>
): ComponentArgument<unknown> | null {
argument_path = [...argument_path];
if (argument_path.length == 0) {
return null;
}
let argument: ComponentArgument<unknown> | null =
this.getArguments()[argument_path.shift() as string];
if (argument_path.length == 0) {
return argument;
}
do {
argument = argument.getSubArgument(
argument_path.shift() as string,
values
);
} while (argument_path.length && argument !== null);
return argument;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 8, 12:33 (16 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1034581
Default Alt Text
(15 KB)
Attached To
Mode
rJDD jdd
Attached
Detach File
Event Timeline
Log In to Comment