From cf3398e7d2f2acc323d33aa7e28e8ff6aafe4638 Mon Sep 17 00:00:00 2001
From: stopachka <stepan.p@gmail.com>
Date: Tue, 26 Nov 2024 13:10:55 -0800
Subject: [PATCH 1/4] roar

---
 client/packages/cli/index.js       | 10 ++++--
 client/packages/core/src/schema.ts | 52 +++++++++++++-----------------
 2 files changed, 30 insertions(+), 32 deletions(-)

diff --git a/client/packages/cli/index.js b/client/packages/cli/index.js
index 495edecd9..2373338e1 100644
--- a/client/packages/cli/index.js
+++ b/client/packages/cli/index.js
@@ -1559,7 +1559,7 @@ function generateSchemaTypescriptFile(id, schema, title, instantModuleName) {
 
 import { i } from "${instantModuleName ?? "@instantdb/core"}";
 
-const graph = i.graph(
+const _schema = i.schema(
 ${
   Object.keys(schema.blobs).length === 1 &&
   Object.keys(schema.blobs)[0] === "$users"
@@ -1584,6 +1584,12 @@ ${
 ${indentLines(JSON.stringify(linksEntriesCode, null, "  "), 1)}
 );
 
-export default graph;
+// This helps Typescript display nicer intellisense
+type _AppSchema = typeof _schema;
+interface AppSchema extends _AppSchema {}
+const schema: AppSchema = _schema;
+
+export type AppSchema; 
+export default schema;
 `;
 }
diff --git a/client/packages/core/src/schema.ts b/client/packages/core/src/schema.ts
index 8f4921e58..ee25711fb 100644
--- a/client/packages/core/src/schema.ts
+++ b/client/packages/core/src/schema.ts
@@ -13,37 +13,15 @@ import {
 // ==========
 // API
 
-
 /**
- * Accepts entities and links and merges them into a single graph definition.
+ * @deprecated Use `i.schema` instead.
  *
- * @see https://instantdb.com/docs/schema#defining-entities
  * @example
- *   export default i.graph(
- *     {
- *       posts: i.entity({
- *         title: i.string(),
- *         body: i.string(),
- *       }),
- *       comments: i.entity({
- *         body: i.string(),
- *       }),
- *     },
- *     {
- *       postsComments: {
- *         forward: {
- *           on: "posts",
- *           has: "many",
- *           label: "comments",
- *         },
- *         reverse: {
- *           on: "comments",
- *           has: "one",
- *           label: "post",
- *         },
- *       },
- *     },
- *   );
+ * // Before 
+ * i.graph(entities, links).withRoomSchema<RoomType>(); 
+ * 
+ * // After 
+ * i.schema({ entities, links, rooms })
  */
 function graph<
   EntitiesWithoutLinks extends EntitiesDef,
@@ -152,8 +130,22 @@ type LinksIndex = Record<
   Record<string, Record<string, { entityName: string; cardinality: string }>>
 >;
 
-/**
- * TODO
+/** 
+ * Lets you define a schema for your database. 
+ * 
+ * You can define entities, links between entities, and if you use 
+ * presence, you can define rooms.
+ * 
+ * You can push this schema to your database with the CLI,
+ * or use it inside `init_experimental`, to get typesafety and autocompletion.
+ *
+ * @see https://instantdb.com/docs/schema
+ * @example
+ *   i.schema({ 
+ *     entities: { }, 
+ *     links: { }, 
+ *     rooms: { }
+ *   }); 
  */
 function schema<
   EntitiesWithoutLinks extends EntitiesDef,

From f6cdf8f5db6fcca719d7953d9a3c6bb7c28d7520 Mon Sep 17 00:00:00 2001
From: stopachka <stepan.p@gmail.com>
Date: Tue, 26 Nov 2024 13:12:30 -0800
Subject: [PATCH 2/4] roar

---
 client/packages/core/src/schema.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/client/packages/core/src/schema.ts b/client/packages/core/src/schema.ts
index ee25711fb..7858778ef 100644
--- a/client/packages/core/src/schema.ts
+++ b/client/packages/core/src/schema.ts
@@ -14,7 +14,8 @@ import {
 // API
 
 /**
- * @deprecated Use `i.schema` instead.
+ * @deprecated 
+ * `i.graph` is deprecated. Use `i.schema` instead.
  *
  * @example
  * // Before 
@@ -22,6 +23,9 @@ import {
  * 
  * // After 
  * i.schema({ entities, links, rooms })
+ * 
+ * @see
+ * https://instantdb.com/docs/schema
  */
 function graph<
   EntitiesWithoutLinks extends EntitiesDef,

From 0dbb2d812c644521160f4b298165eb860bc38509 Mon Sep 17 00:00:00 2001
From: stopachka <stepan.p@gmail.com>
Date: Tue, 26 Nov 2024 13:44:53 -0800
Subject: [PATCH 3/4] roar

---
 client/packages/cli/index.js                  | 64 +++++++++++--------
 .../core/__tests__/src/schema.test.ts         | 19 +++---
 .../strong-init-vite/instant.schema.ts        | 47 +++++++-------
 3 files changed, 66 insertions(+), 64 deletions(-)

diff --git a/client/packages/cli/index.js b/client/packages/cli/index.js
index 2373338e1..1497d8872 100644
--- a/client/packages/cli/index.js
+++ b/client/packages/cli/index.js
@@ -410,8 +410,8 @@ function printDotEnvInfo(source, appId) {
     const otherEnvs = Object.values(rest);
     otherEnvs.sort();
     const otherEnvStr = otherEnvs.map((x) => "  " + chalk.green(x)).join("\n");
-    console.log(`Alternative names: \n${otherEnvStr}`);
-    console.log(terminalLink("Dashboard", appDashUrl(appId)));
+    console.log(`Alternative names: \n${otherEnvStr}\n`);
+    console.log(terminalLink("Dashboard", appDashUrl(appId)) + "\n");
   }
 }
 
@@ -1530,7 +1530,7 @@ function generateSchemaTypescriptFile(id, schema, title, instantModuleName) {
 
   const entitiesObjCode = `{\n${entitiesEntriesCode}\n}`;
 
-  const linksEntriesCode = Object.fromEntries(
+  const linksEntries = Object.fromEntries(
     sortedEntries(schema.refs).map(([_name, config]) => {
       const [, fe, flabel] = config["forward-identity"];
       const [, re, rlabel] = config["reverse-identity"];
@@ -1552,44 +1552,52 @@ function generateSchemaTypescriptFile(id, schema, title, instantModuleName) {
       ];
     }),
   );
+  const linksEntriesCode = JSON.stringify(linksEntries, null, "  ");
 
-  return `
-// ${appDashUrl(id)}
-// Docs: https://www.instantdb.com/docs/schema
-
-import { i } from "${instantModuleName ?? "@instantdb/core"}";
-
-const _schema = i.schema(
-${
-  Object.keys(schema.blobs).length === 1 &&
-  Object.keys(schema.blobs)[0] === "$users"
+  const etypes = Object.keys(schema.blobs);
+  const hasOnlyUserTable = etypes.length === 1 && etypes[0] === "$users";
+  const entitiesComment = hasOnlyUserTable
     ? `
 // This section lets you define entities: think \`posts\`, \`comments\`, etc
 // Take a look at the docs to learn more:
 // https://www.instantdb.com/docs/schema#defining-entities
 `.trim()
-    : ""
-}
-${indentLines(entitiesObjCode, 1)},
-${
-  Object.keys(schema.refs).length === 0
+    : "";
+  const hasNoLinks = Object.keys(linksEntries).length === 0;
+  const linksComment = hasNoLinks
     ? `
-// You can define links here.
-// For example, if \`posts\` should have many \`comments\`.
-// More in the docs:
-// https://www.instantdb.com/docs/schema#defining-links
-`.trim()
-    : ""
-}
-${indentLines(JSON.stringify(linksEntriesCode, null, "  "), 1)}
-);
+  // You can define links here.
+  // For example, if \`posts\` should have many \`comments\`.
+  // More in the docs:
+  // https://www.instantdb.com/docs/schema#defining-links
+  `.trim()
+    : "";
+
+  const roomsComment = `
+// If you use presence, you can define a room schema here
+// https://www.instantdb.com/docs/schema#defining-rooms
+  `.trim();
+
+  return `
+// Docs: https://www.instantdb.com/docs/schema
+
+import { i } from "${instantModuleName ?? "@instantdb/core"}";
+
+const _schema = i.schema({
+  ${entitiesComment}
+  entities: ${entitiesObjCode},
+  ${linksComment}
+  links: ${linksEntriesCode},
+  ${roomsComment}
+  rooms: {}
+});
 
 // This helps Typescript display nicer intellisense
 type _AppSchema = typeof _schema;
 interface AppSchema extends _AppSchema {}
 const schema: AppSchema = _schema;
 
-export type AppSchema; 
+export { type AppSchema }
 export default schema;
 `;
 }
diff --git a/client/packages/core/__tests__/src/schema.test.ts b/client/packages/core/__tests__/src/schema.test.ts
index 5123dfdfe..7efe4e892 100644
--- a/client/packages/core/__tests__/src/schema.test.ts
+++ b/client/packages/core/__tests__/src/schema.test.ts
@@ -1,11 +1,11 @@
 import { test } from "vitest";
 
 import { i } from "../../src";
-import type { InstaQLQueryResult } from "../../src/queryTypes";
+import type { InstaQLResult } from "../../src/queryTypes";
 
 test("runs without exception", () => {
-  const graph = i.graph(
-    {
+  const schema = i.schema({
+    entities: {
       users: i.entity({
         name: i.string(),
         email: i.string().indexed().unique(),
@@ -23,7 +23,7 @@ test("runs without exception", () => {
         body: i.string(),
       }),
     },
-    {
+    links: {
       usersPosts: {
         forward: {
           on: "users",
@@ -73,7 +73,8 @@ test("runs without exception", () => {
         },
       },
     },
-  );
+    rooms: {},
+  });
 
   const demoQuery = {
     users: {
@@ -88,7 +89,7 @@ test("runs without exception", () => {
     },
   };
 
-  type Graph = typeof graph;
+  type Graph = typeof schema;
 
   // Explore derived types
   type Test1 = Graph["entities"]["users"]["links"]["_friends"]["entityName"];
@@ -103,11 +104,7 @@ test("runs without exception", () => {
   // - referrer is NOT an array (because cardinality is 'one')
   // - posts is not an array (because `$first`)
   const queryResult: DemoQueryResult = null as any;
-  type DemoQueryResult = InstaQLQueryResult<
-    Graph["entities"],
-    typeof demoQuery,
-    true
-  >;
+  type DemoQueryResult = InstaQLResult<Graph, typeof demoQuery>;
   queryResult?.users[0].friends[0]._friends[0].bio;
   queryResult?.users[0].posts[0].author?.junk;
 });
diff --git a/client/sandbox/strong-init-vite/instant.schema.ts b/client/sandbox/strong-init-vite/instant.schema.ts
index 83d223d6c..dbd222d2c 100644
--- a/client/sandbox/strong-init-vite/instant.schema.ts
+++ b/client/sandbox/strong-init-vite/instant.schema.ts
@@ -1,33 +1,30 @@
+// Docs: https://www.instantdb.com/docs/schema
+
 import { i } from "@instantdb/react";
 
-const _graph = i.graph(
-  {
-    messages: i.entity({
-      content: i.string(),
-    }),
+const _schema = i.schema({
+  // This section lets you define entities: think `posts`, `comments`, etc
+  // Take a look at the docs to learn more:
+  // https://www.instantdb.com/docs/schema#defining-entities
+  entities: {
     $users: i.entity({
       email: i.string().unique().indexed(),
     }),
   },
-  {
-    messageCreator: {
-      forward: {
-        on: "messages",
-        has: "one",
-        label: "creator",
-      },
-      reverse: {
-        on: "$users",
-        has: "many",
-        label: "createdMessages",
-      },
-    },
-  },
-  
-);
+  // You can define links here.
+  // For example, if `posts` should have many `comments`.
+  // More in the docs:
+  // https://www.instantdb.com/docs/schema#defining-links
+  links: {},
+  // If you use presence, you can define a room schema here
+  // https://www.instantdb.com/docs/schema#defining-rooms
+  rooms: {},
+});
 
-type _Graph = typeof _graph;
+// This helps Typescript display nicer intellisense
+type _AppSchema = typeof _schema;
+interface AppSchema extends _AppSchema {}
+const schema: AppSchema = _schema;
 
-export interface Graph extends _Graph {};
-const graph: Graph = _graph;
-export default graph;
+export { type AppSchema };
+export default schema;

From 73d931bf5f54cd845682ecd0f5e1c149fc2e8f75 Mon Sep 17 00:00:00 2001
From: stopachka <stepan.p@gmail.com>
Date: Tue, 26 Nov 2024 13:56:44 -0800
Subject: [PATCH 4/4] roar

---
 client/sandbox/cli-nodejs/instant.schema.ts   | 11 ++---
 .../strong-init-vite/instant.schema.ts        |  8 +++-
 client/www/pages/docs/cli.md                  | 41 +++++++++++++------
 client/www/pages/docs/patterns.md             | 29 +++++++------
 client/www/pages/docs/schema.md               | 14 ++++---
 5 files changed, 66 insertions(+), 37 deletions(-)

diff --git a/client/sandbox/cli-nodejs/instant.schema.ts b/client/sandbox/cli-nodejs/instant.schema.ts
index 1a31a6a36..238e6e9cb 100644
--- a/client/sandbox/cli-nodejs/instant.schema.ts
+++ b/client/sandbox/cli-nodejs/instant.schema.ts
@@ -1,7 +1,7 @@
 import { i } from "@instantdb/core";
 
-const graph = i.graph(
-  {
+const schema = i.schema({
+  entities: {
     authors: i.entity({
       name: i.any(),
       userId: i.any(),
@@ -14,7 +14,7 @@ const graph = i.graph(
       label: i.any(),
     }),
   },
-  {
+  links: {
     authorsPosts: {
       forward: {
         on: "authors",
@@ -40,6 +40,7 @@ const graph = i.graph(
       },
     },
   },
-);
+  rooms: {},
+});
 
-export default graph;
+export default schema;
diff --git a/client/sandbox/strong-init-vite/instant.schema.ts b/client/sandbox/strong-init-vite/instant.schema.ts
index dbd222d2c..60030bd72 100644
--- a/client/sandbox/strong-init-vite/instant.schema.ts
+++ b/client/sandbox/strong-init-vite/instant.schema.ts
@@ -18,7 +18,13 @@ const _schema = i.schema({
   links: {},
   // If you use presence, you can define a room schema here
   // https://www.instantdb.com/docs/schema#defining-rooms
-  rooms: {},
+  rooms: {
+    chat: {
+      presence: i.entity({
+        nickname: i.string(),
+      }),
+    },
+  },
 });
 
 // This helps Typescript display nicer intellisense
diff --git a/client/www/pages/docs/cli.md b/client/www/pages/docs/cli.md
index f201a4599..6cde7cb87 100644
--- a/client/www/pages/docs/cli.md
+++ b/client/www/pages/docs/cli.md
@@ -15,13 +15,13 @@ You can view all commands and flags with `npx instant-cli -h`.
 
 ## Configuration as code
 
-Instant CLI relies on the presence of two core config files: `instant.schema.ts`, where you define your application's graph structure, and `instant.perms.ts`, where you define access control rules for all of your graph's constructs.
+Instant CLI relies on the presence of two core config files: `instant.schema.ts`, where you define your application's data model, and `instant.perms.ts`, where you define access control rules for all of your graph's constructs.
 
 You can learn more about [schemas here](/docs/schema) here and [permissions here](/docs/permissions).
 
 ## App ID
 
-The CLI looks for `INSTANT_APP_ID` in `process.env`. As a convenience, it will also check for `NEXT_PUBLIC_INSTANT_APP_ID`, `PUBLIC_INSTANT_APP_ID`, and `VITE_INSTANT_APP_ID`
+The CLI looks for `INSTANT_APP_ID` in `process.env`. As a convenience, it will also check for `NEXT_PUBLIC_INSTANT_APP_ID`, `PUBLIC_INSTANT_APP_ID`, and `VITE_INSTANT_APP_ID`.
 
 ## Specifying an auth token
 
@@ -50,7 +50,7 @@ Note, this command will open Instant's dashboard in a browser window and prompt
 npx instant-cli init
 ```
 
-Similar to `git init`, running `instant-cli init` will generate a new app id and add `instant.schema.ts` and `instant.perms.ts` files if none are present in your project's root directory.
+Running `instant-cli init` will help you generate your `instant.schema.ts` and `instant.perms.ts` files. You can either create a new Instant app, or an import an existing one through this flow.
 
 ### Push schema
 
@@ -58,17 +58,17 @@ Similar to `git init`, running `instant-cli init` will generate a new app id and
 npx instant-cli push schema
 ```
 
-`push schema` evals your `instant.schema.ts` file and applies it your app's production database. [Read more about schema as code](/docs/schema).
+`push schema` evaluates your `instant.schema.ts` file and applies it your app's production database. [Read more about schema as code](/docs/schema).
 
 Note, to avoid accidental data loss, `push schema` does not delete entities or fields you've removed from your schema. You can manually delete them in the [Explorer](https://www.instantdb.com/dash?s=main&t=explorer).
 
 Here's an example `instant.schema.ts` file.
 
 ```ts
-import { i } from '@instantdb/core';
+import { i } from '@instantdb/react';
 
-const graph = i.graph(
-  {
+const _schema = i.schema({
+  entities: {
     authors: i.entity({
       userId: i.string(),
       name: i.string(),
@@ -78,7 +78,7 @@ const graph = i.graph(
       content: i.string(),
     }),
   },
-  {
+  links: {
     authorPosts: {
       forward: {
         on: 'authors',
@@ -91,10 +91,23 @@ const graph = i.graph(
         label: 'author',
       },
     },
-  }
-);
+  },
+  rooms: {
+    chat: {
+      presence: i.entity({
+        nickname: i.string()
+      })
+    }
+  },
+});
 
-export default graph;
+// This helps Typescript display nicer intellisense
+type _AppSchema = typeof _schema;
+interface AppSchema extends _AppSchema {}
+const schema: AppSchema = _schema;
+
+export { type AppSchema };
+export default schema;
 ```
 
 ### Push perms
@@ -103,12 +116,12 @@ export default graph;
 npx instant-cli push perms
 ```
 
-`push perms` evals your `instant.perms.ts` file and applies it your app's production database. `instant.perms.ts` should export an object implementing Instant's standard permissions CEL+JSON format. [Read more about permissions in Instant](/docs/permissions).
+`push perms` evaluates your `instant.perms.ts` file and applies it your app's production database. `instant.perms.ts` should export an object implementing Instant's standard permissions CEL+JSON format. [Read more about permissions in Instant](/docs/permissions).
 
 Here's an example `instant.perms.ts` file.
 
 ```ts
-export default {
+const rules = {
   allow: {
     posts: {
       bind: ['isAuthor', "auth.id in data.ref('authors.userId')"],
@@ -121,6 +134,8 @@ export default {
     },
   },
 };
+
+export default rules;
 ```
 
 ### Pull: migrating from the dashboard
diff --git a/client/www/pages/docs/patterns.md b/client/www/pages/docs/patterns.md
index e053c65b3..144f98c54 100644
--- a/client/www/pages/docs/patterns.md
+++ b/client/www/pages/docs/patterns.md
@@ -41,10 +41,10 @@ permissions. Here's an example of limiting a user to creating at most 2 todos.
 ```typescript
 // instant.schema.ts
 // Here we define users, todos, and a link between them.
-import { i } from '@instantdb/core';
+import { i } from '@instantdb/react';
 
-const graph = i.graph(
-  {
+const schema = i.schema({
+  entities: {
     users: i.entity({
       email: i.string(),
     }),
@@ -52,7 +52,7 @@ const graph = i.graph(
       label: i.string(),
     }),
   },
-  {
+  links: {
     userTodos: {
       forward: {
         on: 'users',
@@ -65,23 +65,26 @@ const graph = i.graph(
         label: 'owner',
       },
     },
-  }
-);
+  },
+  rooms: {}
+});
 
-export default graph;
+export default schema;
 ```
 
 ```typescript
-// instant.schema.ts
+// instant.perms.ts
 // And now we reference the `owner` link for todos to check the number
 // of todos a user has created.
 // (Note): Make sure the `owner` link is already defined in the schema.
 // before you can reference it in the permissions.
-export {
-  "todos": {
-    "allow": {
-      "create": "size(data.ref('owner.todos.id')) <= 2",
+const rules = {
+  todos: {
+    allow: {
+      create: "size(data.ref('owner.todos.id')) <= 2",
     }
   }
-}
+};
+
+export default rules;
 ```
diff --git a/client/www/pages/docs/schema.md b/client/www/pages/docs/schema.md
index 16ee61e97..66738f842 100644
--- a/client/www/pages/docs/schema.md
+++ b/client/www/pages/docs/schema.md
@@ -6,17 +6,18 @@ title: Schema-as-code
 
 This file lives in the root of your project and will be consumed by [the Instant CLI](/docs/cli). You can apply your schema to the production database with `npx instant-cli push schema`.
 
-The default export of `instant.schema.ts` should always be the result of a call to `i.graph`.
+The default export of `instant.schema.ts` should always be the result of a call to `i.schema`.
 
 ```typescript
 // instant.schema.ts
 
 import { i } from '@instantdb/core';
 
-const graph = i.graph(
-  entitiesMap, // a map of `i.entity` definitions, see "Defining entities" below
-  linksMap // a description of links between your app's entities, see "Defining links" below
-);
+const schema = i.graph(
+  entities: entitiesMap, // a map of `i.entity` definitions, see "Defining entities" below
+  links: linksMap // a description of links between your app's entities, see "Defining links" below, 
+  rooms: roomsMap // a description of your presence state
+});
 
 export default graph;
 ```
@@ -107,6 +108,9 @@ Links are bidirectional, and you can specify a name and cardinality for both the
 }
 ```
 
+## Defining rooms
+
+
 ## An example schema file
 
 Below we demonstrate a data model for a blog. First we define our core entities: authors, posts and tags.