diff --git a/.changeset/warm-carrots-compare.md b/.changeset/warm-carrots-compare.md
new file mode 100644
index 0000000000..299e2cc3ed
--- /dev/null
+++ b/.changeset/warm-carrots-compare.md
@@ -0,0 +1,9 @@
+---
+"@latticexyz/config": patch
+"@latticexyz/store-sync": patch
+"@latticexyz/store": patch
+"@latticexyz/world": patch
+---
+
+Add a strongly typed `namespaceLabel` to the table config output.
+It corresponds to the `label` of the namespace the table belongs to and can't be set manually.
diff --git a/packages/cli/src/deploy/getTables.ts b/packages/cli/src/deploy/getTables.ts
index 27d56f3449..91eb894558 100644
--- a/packages/cli/src/deploy/getTables.ts
+++ b/packages/cli/src/deploy/getTables.ts
@@ -15,8 +15,8 @@ import {
import { Schema, Table } from "@latticexyz/config";
import storeConfig from "@latticexyz/store/mud.config";
-// TODO: add label once we register it onchain
-type DeployedTable = Omit
;
+// TODO: add label and namespaceLabel once we register it onchain
+type DeployedTable = Omit;
export async function getTables({
client,
diff --git a/packages/config/src/common.ts b/packages/config/src/common.ts
index 3577a2318d..d56e487acd 100644
--- a/packages/config/src/common.ts
+++ b/packages/config/src/common.ts
@@ -27,6 +27,7 @@ export type Table = {
readonly label: string;
readonly type: satisfy;
readonly namespace: string;
+ readonly namespaceLabel: string;
readonly name: string;
readonly tableId: Hex;
readonly schema: Schema;
diff --git a/packages/store-sync/src/common.ts b/packages/store-sync/src/common.ts
index 740935e2c9..721a4491b1 100644
--- a/packages/store-sync/src/common.ts
+++ b/packages/store-sync/src/common.ts
@@ -25,8 +25,8 @@ export const internalTableIds = Object.values(mudTables).map((table) => table.ta
export type ChainId = number;
export type WorldId = `${ChainId}:${Address}`;
-// TODO: add label once we register it onchain
-export type DeployedTable = Omit;
+// TODO: add label and namespaceLabel once we register it onchain
+export type DeployedTable = Omit;
export type TableRecord = {
readonly key: getSchemaPrimitives>;
diff --git a/packages/store-sync/src/zustand/createStorageAdapter.test.ts b/packages/store-sync/src/zustand/createStorageAdapter.test.ts
index b12ff57662..4ff4863559 100644
--- a/packages/store-sync/src/zustand/createStorageAdapter.test.ts
+++ b/packages/store-sync/src/zustand/createStorageAdapter.test.ts
@@ -62,6 +62,7 @@ describe("createStorageAdapter", async () => {
"label": "Position",
"name": "Position",
"namespace": "",
+ "namespaceLabel": "",
"schema": {
"player": {
"internalType": "address",
@@ -113,6 +114,7 @@ describe("createStorageAdapter", async () => {
"label": "Position",
"name": "Position",
"namespace": "",
+ "namespaceLabel": "",
"schema": {
"player": {
"internalType": "address",
@@ -164,6 +166,7 @@ describe("createStorageAdapter", async () => {
"label": "Position",
"name": "Position",
"namespace": "",
+ "namespaceLabel": "",
"schema": {
"player": {
"internalType": "address",
@@ -215,6 +218,7 @@ describe("createStorageAdapter", async () => {
"label": "Position",
"name": "Position",
"namespace": "",
+ "namespaceLabel": "",
"schema": {
"player": {
"internalType": "address",
diff --git a/packages/store/ts/config/v2/input.ts b/packages/store/ts/config/v2/input.ts
index 43509ab357..ca103761bf 100644
--- a/packages/store/ts/config/v2/input.ts
+++ b/packages/store/ts/config/v2/input.ts
@@ -31,6 +31,11 @@ export type TableInput = {
* Defaults to the nearest namespace in the config or root namespace if not set.
*/
readonly namespace?: string;
+ /**
+ * Human-readable namespace label.
+ * Defaults to the nearest namespace in the config or root namespace if not set.
+ */
+ readonly namespaceLabel?: string;
/**
* Table name used in table's resource ID.
* Defaults to the first 16 characters of `label` if not set.
@@ -47,7 +52,7 @@ export type TableShorthandInput = SchemaInput | string;
export type TablesInput = {
// remove label and namespace as these are set contextually
// and allow defining a table using shorthand
- readonly [label: string]: Omit | TableShorthandInput;
+ readonly [label: string]: Omit | TableShorthandInput;
};
export type CodegenInput = Partial;
diff --git a/packages/store/ts/config/v2/namespace.ts b/packages/store/ts/config/v2/namespace.ts
index 4d0f22a40d..fd133a2f8d 100644
--- a/packages/store/ts/config/v2/namespace.ts
+++ b/packages/store/ts/config/v2/namespace.ts
@@ -36,7 +36,7 @@ export type resolveNamespace = input
{
readonly [label in keyof input["tables"]]: mergeIfUndefined<
expandTableShorthand ,
- { readonly namespace: string }
+ { readonly namespace: string; readonly namespaceLabel: input["label"] }
>;
},
scope
@@ -49,14 +49,14 @@ export function resolveNamespace {
- const label = input.label;
- const namespace = input.namespace ?? label.slice(0, 14);
+ const namespaceLabel = input.label;
+ const namespace = input.namespace ?? namespaceLabel.slice(0, 14);
return {
- label,
+ label: namespaceLabel,
namespace,
tables: resolveTables(
flatMorph(input.tables ?? {}, (label, table) => {
- return [label, mergeIfUndefined(expandTableShorthand(table, scope), { namespace })];
+ return [label, mergeIfUndefined(expandTableShorthand(table, scope), { namespace, namespaceLabel })];
}),
scope,
),
diff --git a/packages/store/ts/config/v2/store.test.ts b/packages/store/ts/config/v2/store.test.ts
index 6943925c8e..d5efe6e04e 100644
--- a/packages/store/ts/config/v2/store.test.ts
+++ b/packages/store/ts/config/v2/store.test.ts
@@ -28,6 +28,7 @@ describe("defineStore", () => {
label: "Example",
type: "table",
namespace: "",
+ namespaceLabel: "",
name: "Example",
tableId: "0x746200000000000000000000000000004578616d706c65000000000000000000",
schema: {
@@ -47,6 +48,7 @@ describe("defineStore", () => {
label: "Example",
type: "table",
namespace: "",
+ namespaceLabel: "",
name: "Example",
tableId: "0x746200000000000000000000000000004578616d706c65000000000000000000",
schema: {
@@ -80,6 +82,7 @@ describe("defineStore", () => {
readonly Example: {
readonly label: "Example"
readonly type: "table"
+ readonly namespaceLabel: ""
readonly namespace: string
readonly name: string
readonly tableId: \`0x\${string}\`
@@ -113,6 +116,7 @@ describe("defineStore", () => {
readonly Example: {
readonly label: "Example"
readonly type: "table"
+ readonly namespaceLabel: ""
readonly namespace: string
readonly name: string
readonly tableId: \`0x\${string}\`
@@ -180,6 +184,7 @@ describe("defineStore", () => {
label: "Example",
type: "table",
namespace: "",
+ namespaceLabel: "root",
name: "Example",
tableId: "0x746200000000000000000000000000004578616d706c65000000000000000000",
schema: {
@@ -199,6 +204,7 @@ describe("defineStore", () => {
label: "Example",
type: "table",
namespace: "",
+ namespaceLabel: "root",
name: "Example",
tableId: "0x746200000000000000000000000000004578616d706c65000000000000000000",
schema: {
@@ -232,6 +238,7 @@ describe("defineStore", () => {
readonly Example: {
readonly label: "Example"
readonly type: "table"
+ readonly namespaceLabel: "root"
readonly namespace: string
readonly name: string
readonly tableId: \`0x\${string}\`
@@ -265,6 +272,7 @@ describe("defineStore", () => {
readonly root__Example: {
readonly label: "Example"
readonly type: "table"
+ readonly namespaceLabel: "root"
readonly namespace: string
readonly name: string
readonly tableId: \`0x\${string}\`
diff --git a/packages/store/ts/config/v2/table.test.ts b/packages/store/ts/config/v2/table.test.ts
index 69d99eafea..690822c5cf 100644
--- a/packages/store/ts/config/v2/table.test.ts
+++ b/packages/store/ts/config/v2/table.test.ts
@@ -57,6 +57,7 @@ describe("resolveTable", () => {
label: "",
type: "table",
namespace: "" as string,
+ namespaceLabel: "",
name: "" as string,
tableId: resourceToHex({ type: "table", namespace: "", name: "" }),
schema: {
@@ -82,6 +83,7 @@ describe("resolveTable", () => {
label: "",
type: "table",
namespace: "" as string,
+ namespaceLabel: "",
name: "" as string,
tableId: resourceToHex({ type: "table", namespace: "", name: "" }),
schema: {
@@ -113,6 +115,7 @@ describe("resolveTable", () => {
label: "",
type: "table",
namespace: "" as string,
+ namespaceLabel: "",
name: "" as string,
tableId: resourceToHex({ type: "table", namespace: "", name: "" }),
schema: {
diff --git a/packages/store/ts/config/v2/table.ts b/packages/store/ts/config/v2/table.ts
index 6937a671ba..2287cd81ec 100644
--- a/packages/store/ts/config/v2/table.ts
+++ b/packages/store/ts/config/v2/table.ts
@@ -99,6 +99,18 @@ export function validateTable (
if (hasOwnKey(input, "namespace") && typeof input.namespace === "string" && input.namespace.length > 14) {
throw new Error(`Table \`namespace\` must fit into a \`bytes14\`, but "${input.namespace}" is too long.`);
}
+
+ if (
+ hasOwnKey(input, "namespaceLabel") &&
+ typeof input.namespaceLabel === "string" &&
+ (!hasOwnKey(input, "namespace") || typeof input.namespace !== "string") &&
+ input.namespaceLabel.length > 14
+ ) {
+ throw new Error(
+ `Table \`namespace\` defaults to \`namespaceLabel\`, but must fit into a \`bytes14\` and "${input.namespaceLabel}" is too long. Provide explicit \`namespace\` override.`,
+ );
+ }
+
if (hasOwnKey(input, "name") && typeof input.name === "string" && input.name.length > 16) {
throw new Error(`Table \`name\` must fit into a \`bytes16\`, but "${input.name}" is too long.`);
}
@@ -140,6 +152,9 @@ export type resolveTable = input extends Tab
? {
readonly label: input["label"];
readonly type: undefined extends input["type"] ? typeof TABLE_DEFAULTS.type : input["type"];
+ readonly namespaceLabel: undefined extends input["namespaceLabel"]
+ ? typeof TABLE_DEFAULTS.namespace
+ : input["namespaceLabel"];
readonly namespace: string;
readonly name: string;
readonly tableId: Hex;
@@ -156,16 +171,18 @@ export function resolveTable {
+ const namespaceLabel = input.namespaceLabel ?? TABLE_DEFAULTS.namespace;
+ const namespace = input.namespace ?? namespaceLabel;
const label = input.label;
- const type = input.type ?? TABLE_DEFAULTS.type;
- const namespace = input.namespace ?? TABLE_DEFAULTS.namespace;
const name = input.name ?? label.slice(0, 16);
+ const type = input.type ?? TABLE_DEFAULTS.type;
const tableId = resourceToHex({ type, namespace, name });
return {
label,
type,
namespace,
+ namespaceLabel,
name,
tableId,
schema: resolveSchema(input.schema, scope),
diff --git a/packages/world/ts/config/v2/world.test.ts b/packages/world/ts/config/v2/world.test.ts
index 90c6a2aefd..222fc2438f 100644
--- a/packages/world/ts/config/v2/world.test.ts
+++ b/packages/world/ts/config/v2/world.test.ts
@@ -143,6 +143,7 @@ describe("defineWorld", () => {
Example: {
label: "Example",
type: "table",
+ namespaceLabel: "",
namespace: "",
name: "Example",
tableId: "0x746200000000000000000000000000004578616d706c65000000000000000000",
@@ -163,6 +164,7 @@ describe("defineWorld", () => {
Example: {
label: "Example",
type: "table",
+ namespaceLabel: "",
namespace: "",
name: "Example",
tableId: "0x746200000000000000000000000000004578616d706c65000000000000000000",
@@ -205,6 +207,7 @@ describe("defineWorld", () => {
readonly Example: {
readonly label: "Example"
readonly type: "table"
+ readonly namespaceLabel: ""
readonly namespace: string
readonly name: string
readonly tableId: \`0x\${string}\`
@@ -255,6 +258,7 @@ describe("defineWorld", () => {
readonly Example: {
readonly label: "Example"
readonly type: "table"
+ readonly namespaceLabel: ""
readonly namespace: string
readonly name: string
readonly tableId: \`0x\${string}\`
@@ -324,6 +328,7 @@ describe("defineWorld", () => {
Example: {
label: "Example",
type: "table",
+ namespaceLabel: "root",
namespace: "",
name: "Example",
tableId: "0x746200000000000000000000000000004578616d706c65000000000000000000",
@@ -344,6 +349,7 @@ describe("defineWorld", () => {
root__Example: {
label: "Example",
type: "table",
+ namespaceLabel: "root",
namespace: "",
name: "Example",
tableId: "0x746200000000000000000000000000004578616d706c65000000000000000000",
@@ -386,6 +392,7 @@ describe("defineWorld", () => {
readonly root__Example: {
readonly label: "Example"
readonly type: "table"
+ readonly namespaceLabel: "root"
readonly namespace: string
readonly name: string
readonly tableId: \`0x\${string}\`
@@ -436,6 +443,7 @@ describe("defineWorld", () => {
readonly Example: {
readonly label: "Example"
readonly type: "table"
+ readonly namespaceLabel: "root"
readonly namespace: string
readonly name: string
readonly tableId: \`0x\${string}\`