Skip to content

Commit

Permalink
start namespace helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
holic committed Jul 5, 2024
1 parent afde0de commit b5d1ca7
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 203 deletions.
57 changes: 57 additions & 0 deletions packages/store/ts/config/v2/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ErrorMessage, flatMorph } from "@arktype/util";
import { get, hasOwnKey, mergeIfUndefined } from "./generics";
import { CONFIG_DEFAULTS } from "./defaults";
import { NamespaceInput } from "./input";
import { resolveTables, validateTables } from "./tables";
import { AbiTypeScope, Scope } from "./scope";

export type validateNamespace<input, scope extends Scope = AbiTypeScope> = {
[key in keyof input]: key extends "tables"
? validateTables<input[key], scope>
: key extends keyof NamespaceInput
? NamespaceInput[key]
: ErrorMessage<`\`${key & string}\` is not a valid namespace config option.`>;
};

export function validateNamespace<scope extends Scope = AbiTypeScope>(
input: unknown,
scope: scope,
): asserts input is NamespaceInput {
if (hasOwnKey(input, "namespace") && typeof input.namespace === "string" && input.namespace.length > 14) {
throw new Error(`\`namespace\` must fit into a \`bytes14\`, but "${input.namespace}" is too long.`);
}
if (hasOwnKey(input, "tables")) {
validateTables(input.tables, scope);
}
}

export type resolveNamespace<input, scope extends Scope = AbiTypeScope> = {
readonly namespace: "namespace" extends keyof input ? input["namespace"] : CONFIG_DEFAULTS["namespace"];
readonly tables: "tables" extends keyof input
? resolveTables<
{
[label in keyof input["tables"]]: mergeIfUndefined<
input["tables"][label],
{ label: label; namespace: get<input, "namespace"> }
>;
},
scope
>
: {};
};

export function resolveNamespace<const input extends NamespaceInput, scope extends Scope = AbiTypeScope>(
input: input,
scope: scope,
): resolveNamespace<input, scope> {
return {
namespace: input.namespace ?? CONFIG_DEFAULTS["namespace"],
tables: resolveTables(
flatMorph(input.tables ?? {}, (label, table) => {
const namespace = input.namespace;
return [label, mergeIfUndefined(table, { label, namespace })];
}),
scope,
),
} as never;
}
87 changes: 0 additions & 87 deletions packages/store/ts/config/v2/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,91 +559,4 @@ describe("defineStore", () => {
}),
).type.errors("`invalidOption` is not a valid Store config option.");
});

it("should namespace output directories for tables", () => {
const config = defineStore({
namespace: "app",
codegen: {
namespaceDirectories: true,
},
tables: {
NamespaceDir: {
schema: { name: "string" },
key: [],
},
NotNamespaceDir: {
schema: { name: "string" },
key: [],
codegen: {
outputDirectory: "tables",
},
},
},
});

const expected = {
sourceDirectory: "src",
tables: {
NamespaceDir: {
tableId: resourceToHex({ type: "table", namespace: "app", name: "NamespaceDir" }),
schema: {
name: {
type: "string",
internalType: "string",
},
},
key: [],
label: "NamespaceDir",
name: "NamespaceDir",
namespace: "app",
codegen: {
...TABLE_CODEGEN_DEFAULTS,
dataStruct: false as boolean,
outputDirectory: "app/tables" as string,
},
type: "table",
deploy: TABLE_DEPLOY_DEFAULTS,
},
NotNamespaceDir: {
tableId: resourceToHex({ type: "table", namespace: "app", name: "NotNamespaceDir" }),
schema: {
name: {
type: "string",
internalType: "string",
},
},
key: [],
label: "NotNamespaceDir",
name: "NotNamespaceDir",
namespace: "app",
codegen: {
...TABLE_CODEGEN_DEFAULTS,
dataStruct: false as boolean,
outputDirectory: "tables",
},
type: "table",
deploy: TABLE_DEPLOY_DEFAULTS,
},
},
userTypes: {},
enums: {},
enumValues: {},
namespace: "app",
codegen: {
...CODEGEN_DEFAULTS,
namespaceDirectories: true,
},
} as const;

// Running attest on the whole object is hard to parse when it fails, so test the inner objects first
attest<typeof expected.codegen>(config.codegen).equals(expected.codegen);
attest<typeof expected.tables.NamespaceDir.codegen>(config.tables.NamespaceDir.codegen).equals(
expected.tables.NamespaceDir.codegen,
);
attest<typeof expected.tables.NotNamespaceDir.codegen>(config.tables.NotNamespaceDir.codegen).equals(
expected.tables.NotNamespaceDir.codegen,
);

attest<typeof expected>(config).equals(expected);
});
});
93 changes: 32 additions & 61 deletions packages/store/ts/config/v2/store.ts
Original file line number Diff line number Diff line change
@@ -1,94 +1,65 @@
import { ErrorMessage, evaluate, flatMorph, narrow } from "@arktype/util";
import { get, hasOwnKey, mergeIfUndefined } from "./generics";
import { ErrorMessage, evaluate, narrow } from "@arktype/util";
import { get, hasOwnKey } from "./generics";
import { UserTypes } from "./output";
import { CONFIG_DEFAULTS } from "./defaults";
import { StoreInput } from "./input";
import { resolveTables, validateTables } from "./tables";
import { validateTables } from "./tables";
import { scopeWithUserTypes, validateUserTypes } from "./userTypes";
import { mapEnums, resolveEnums, scopeWithEnums } from "./enums";
import { resolveCodegen } from "./codegen";
import { resolveNamespace, validateNamespace } from "./namespace";

export type extendedScope<input> = scopeWithEnums<get<input, "enums">, scopeWithUserTypes<get<input, "userTypes">>>;

export function extendedScope<input>(input: input): extendedScope<input> {
return scopeWithEnums(get(input, "enums"), scopeWithUserTypes(get(input, "userTypes")));
}

export type validateStore<store> = {
[key in keyof store]: key extends "tables"
? validateTables<store[key], extendedScope<store>>
export type validateStore<input> = {
[key in keyof input]: key extends "tables"
? validateTables<input[key], extendedScope<input>>
: key extends "userTypes"
? UserTypes
: key extends "enums"
? narrow<store[key]>
? narrow<input[key]>
: key extends keyof StoreInput
? StoreInput[key]
: ErrorMessage<`\`${key & string}\` is not a valid Store config option.`>;
};

export function validateStore(store: unknown): asserts store is StoreInput {
const scope = extendedScope(store);
if (hasOwnKey(store, "tables")) {
validateTables(store.tables, scope);
}
export function validateStore(input: unknown): asserts input is StoreInput {
const scope = extendedScope(input);
validateNamespace(input, scope);

if (hasOwnKey(store, "userTypes")) {
validateUserTypes(store.userTypes);
if (hasOwnKey(input, "userTypes")) {
validateUserTypes(input.userTypes);
}
}

export type resolveStore<store> = {
readonly sourceDirectory: "sourceDirectory" extends keyof store
? store["sourceDirectory"]
export type resolveStore<input> = resolveNamespace<input> & {
readonly sourceDirectory: "sourceDirectory" extends keyof input
? input["sourceDirectory"]
: CONFIG_DEFAULTS["sourceDirectory"];
readonly tables: "tables" extends keyof store
? resolveTables<
{
[label in keyof store["tables"]]: mergeIfUndefined<
store["tables"][label],
{ label: label; namespace: get<store, "namespace"> }
>;
},
extendedScope<store>
>
: {};
readonly userTypes: "userTypes" extends keyof store ? store["userTypes"] : {};
readonly enums: "enums" extends keyof store ? evaluate<resolveEnums<store["enums"]>> : {};
readonly enumValues: "enums" extends keyof store ? evaluate<mapEnums<store["enums"]>> : {};
readonly namespace: "namespace" extends keyof store ? store["namespace"] : CONFIG_DEFAULTS["namespace"];
readonly codegen: "codegen" extends keyof store ? resolveCodegen<store["codegen"]> : resolveCodegen<{}>;
readonly userTypes: "userTypes" extends keyof input ? input["userTypes"] : {};
readonly enums: "enums" extends keyof input ? evaluate<resolveEnums<input["enums"]>> : {};
readonly enumValues: "enums" extends keyof input ? evaluate<mapEnums<input["enums"]>> : {};
readonly codegen: "codegen" extends keyof input ? resolveCodegen<input["codegen"]> : resolveCodegen<{}>;
};

export function resolveStore<const store extends StoreInput>(store: store): resolveStore<store> {
// TODO: default `namespaceDirectories` to true if using top-level `namespaces` key (once its migrated to store)
const codegen = resolveCodegen(store.codegen);
export function resolveStore<const input extends StoreInput>(input: input): resolveStore<input> {
const scope = extendedScope(input);
const namespace = resolveNamespace(input, scope);
return {
sourceDirectory: store.sourceDirectory ?? CONFIG_DEFAULTS["sourceDirectory"],
tables: resolveTables(
flatMorph(store.tables ?? {}, (label, table) => {
const namespace = store.namespace;
return [
label,
mergeIfUndefined(table, {
namespace: namespace,
label,
codegen: mergeIfUndefined(table.codegen ?? {}, {
outputDirectory: codegen.namespaceDirectories && namespace?.length ? `${namespace}/tables` : "tables",
}),
}),
];
}),
extendedScope(store),
),
userTypes: store.userTypes ?? {},
enums: resolveEnums(store.enums ?? {}),
enumValues: mapEnums(store.enums ?? {}),
namespace: store.namespace ?? CONFIG_DEFAULTS["namespace"],
codegen,
...namespace,
sourceDirectory: input.sourceDirectory ?? CONFIG_DEFAULTS["sourceDirectory"],
userTypes: input.userTypes ?? {},
enums: resolveEnums(input.enums ?? {}),
enumValues: mapEnums(input.enums ?? {}),
codegen: resolveCodegen(input.codegen),
} as never;
}

export function defineStore<const store>(store: validateStore<store>): resolveStore<store> {
validateStore(store);
return resolveStore(store) as never;
export function defineStore<const input>(input: validateStore<input>): resolveStore<input> {
validateStore(input);
return resolveStore(input) as never;
}
2 changes: 1 addition & 1 deletion packages/store/ts/config/v2/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type resolveTables<tables, scope extends Scope = AbiTypeScope> = evaluate

export function resolveTables<tables extends TablesInput, scope extends Scope = AbiTypeScope>(
tables: tables,
scope: scope = AbiTypeScope as unknown as scope,
scope: scope,
): resolveTables<tables, scope> {
if (!isObject(tables)) {
throw new Error(`Expected tables config, received ${JSON.stringify(tables)}`);
Expand Down
2 changes: 1 addition & 1 deletion packages/world/ts/actions/callFrom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ async function retrieveSystemFunctionFromContract(
worldAddress: Hex,
worldFunctionSelector: Hex,
): Promise<SystemFunction> {
const table = worldConfig.tables.world__FunctionSelectors;
const table = worldConfig.tables.FunctionSelectors;

const keySchema = getSchemaTypes(getKeySchema(table));
const valueSchema = getSchemaTypes(getValueSchema(table));
Expand Down
Loading

0 comments on commit b5d1ca7

Please sign in to comment.