Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Launch-Ready Strong Init #633

Merged
merged 13 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
44 changes: 31 additions & 13 deletions client/packages/admin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,29 +175,47 @@ async function jsonFetch(
* Visit https://instantdb.com/dash to get your `appId` :)
*
* @example
* const db = init({ appId: "my-app-id" })
* import { init } from "@instantdb/admin"
*
* // You can also provide a schema for type safety and editor autocomplete!
* const db = init({
* appId: "my-app-id",
* adminToken: process.env.INSTANT_ADMIN_TOKEN
* })
*
* type Schema = {
* goals: {
* title: string
* }
* }
* // You can also provide a schema for type safety and editor autocomplete!
*
* const db = init<Schema>({ appId: "my-app-id" })
* import { init } from "@instantdb/admin"
* import schema from ""../instant.schema.ts";
*
* const db = init({
* appId: "my-app-id",
* adminToken: process.env.INSTANT_ADMIN_TOKEN,
* schema,
* })
* // To learn more: https://instantdb.com/docs/modeling-data
*/
function init<Schema extends {} = {}>(config: Config) {
return new InstantAdmin<Schema, false>(config);
}

function init_experimental<
function init<
Schema extends InstantSchemaDef<any, any, any> = InstantUnknownSchema,
>(config: InstantConfig<Schema>) {
return new InstantAdminDatabase<Schema>(config);
}

/**
* @deprecated
* `init_experimental` is deprecated. You can replace it with `init`.
*
* @example
*
* // Before
* import { init_experimental } from "@instantdb/admin"
* const db = init_experimental({ ... });
*
* // After
* import { init } from "@instantdb/admin"
* const db = init({ ... });
*/
const init_experimental = init;

/**
*
* The first step: init your application!
Expand Down
100 changes: 72 additions & 28 deletions client/packages/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,10 @@ program
.command("login")
.description("Log into your account")
.option("-p --print", "Prints the auth token into the console.")
.action(login);
.action(async (opts) => {
console.log("Let's log you in!");
await login(opts);
});

program
.command("init")
Expand Down Expand Up @@ -438,7 +441,7 @@ function printDotEnvInfo(envType, appId) {
otherEnvs.sort();
const otherEnvStr = otherEnvs.map((x) => " " + chalk.green(x)).join("\n");
console.log(`Alternative names: \n${otherEnvStr} \n`);
console.log(terminalLink("Dashboard", appDashUrl(appId)) + '\n');
console.log(terminalLink("Dashboard", appDashUrl(appId)) + "\n");
}

async function handleEnvFile(pkgAndAuthInfo, appId) {
Expand Down Expand Up @@ -511,7 +514,7 @@ async function login(options) {
if (!registerRes.ok) return;

const { secret, ticket } = registerRes.data;
console.log("Let's log you in!");

const ok = await promptOk(
`This will open instantdb.com in your browser, OK to proceed?`,
);
Expand All @@ -534,6 +537,7 @@ async function login(options) {
await saveConfigAuthToken(token);
console.log(chalk.green(`Successfully logged in as ${email}!`));
}
return token;
}

async function getOrInstallInstantModuleWithErrorLogging(pkgDir) {
Expand Down Expand Up @@ -718,7 +722,7 @@ async function resolvePackageAndAuthInfoWithErrorLogging() {
if (!instantModuleName) {
return;
}
const authToken = await readConfigAuthTokenWithErrorLogging();
const authToken = await readAuthTokenOrLoginWithErrorLogging();
if (!authToken) {
return;
}
Expand Down Expand Up @@ -1129,7 +1133,10 @@ async function pushPerms(appId) {

if (!prodPerms.ok) return;

const diffedStr = jsonDiff.diffString(prodPerms.data.perms || {}, perms || {});
const diffedStr = jsonDiff.diffString(
prodPerms.data.perms || {},
perms || {},
);
if (!diffedStr.length) {
console.log("No perms changes detected. Exiting.");
return;
Expand Down Expand Up @@ -1293,21 +1300,21 @@ async function promptOk(message) {
}

/**
* We need to do a bit of a hack of `@instantdb/react-native`.
*
* If a user writes import { i } from '@instantdb/react-native'
*
* We will fail to evaluate the file. This is because
* `@instantdb/react-native` brings in `react-native`, which
* does not run in a node context.
*
* To bypass this, we have a 'cli' module inside `react-native`, which
* We need to do a bit of a hack of `@instantdb/react-native`.
*
* If a user writes import { i } from '@instantdb/react-native'
*
* We will fail to evaluate the file. This is because
* `@instantdb/react-native` brings in `react-native`, which
* does not run in a node context.
*
* To bypass this, we have a 'cli' module inside `react-native`, which
* has all the necessary imports
*/
function transformImports(code) {
function transformImports(code) {
return code.replace(
/"@instantdb\/react-native"/g,
'"@instantdb/react-native/dist/cli"'
/"@instantdb\/react-native"/g,
'"@instantdb/react-native/dist/cli"',
);
}

Expand Down Expand Up @@ -1407,6 +1414,7 @@ async function readConfigAuthToken() {

return authToken;
}

async function readConfigAuthTokenWithErrorLogging() {
const token = await readConfigAuthToken();
if (!token) {
Expand All @@ -1417,6 +1425,14 @@ async function readConfigAuthTokenWithErrorLogging() {
return token;
}

async function readAuthTokenOrLoginWithErrorLogging() {
const token = await readConfigAuthToken();
if (token) return token;
console.log(`Looks like you are not logged in...`);
console.log(`Let's log in!`);
return await login({});
}

async function saveConfigAuthToken(authToken) {
const authPaths = getAuthPaths();

Expand Down Expand Up @@ -1453,13 +1469,6 @@ function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

function indentLines(s, n) {
return s
.split("\n")
.map((c) => `${" ".repeat(n)}${c}`)
.join("\n");
}

// attr helpers

function attrFwdLabel(attr) {
Expand Down Expand Up @@ -1578,6 +1587,24 @@ export default rules;
`.trim();
}

function inferredType(config) {
const inferredList = config["inferred-types"];
const hasJustOne = inferredList?.length === 1;
if (!hasJustOne) return null;
return inferredList[0];
}

function deriveClientType(attr) {
if (attr["checked-data-type"]) {
return { type: attr["checked-data-type"], origin: "checked" };
}
const inferred = inferredType(attr);
if (inferred) {
return { type: inferred, origin: "inferred" };
}
return { type: "any", origin: "unknown" };
}

function schemaBlobToCodeStr(name, attrs) {
// a block of code for each entity
return [
Expand All @@ -1591,7 +1618,7 @@ function schemaBlobToCodeStr(name, attrs) {
sortedEntries(attrs)
.filter(([name]) => name !== "id")
.map(([name, config]) => {
const type = config["checked-data-type"] || "any";
const { type } = deriveClientType(config);

return [
` `,
Expand Down Expand Up @@ -1679,6 +1706,10 @@ function roomsCodeStr(rooms) {
return ret;
}

function easyPlural(strn, n) {
return n === 1 ? strn : strn + "s";
}

function generateSchemaTypescriptFile(
prevSchema,
newSchema,
Expand All @@ -1688,16 +1719,29 @@ function generateSchemaTypescriptFile(
const entitiesEntriesCode = sortedEntries(newSchema.blobs)
.map(([name, attrs]) => schemaBlobToCodeStr(name, attrs))
.join("\n");
const inferredAttrs = Object.values(newSchema.blobs)
.flatMap(Object.values)
.filter(
(attr) =>
attrFwdLabel(attr) !== "id" &&
deriveClientType(attr).origin === "inferred",
);

const entitiesObjCode = `{\n${entitiesEntriesCode}\n}`;
const etypes = Object.keys(newSchema.blobs);
const hasOnlyUserTable = etypes.length === 1 && etypes[0] === "$users";
const entitiesComment = hasOnlyUserTable
? `
const entitiesComment =
inferredAttrs.length > 0
? `// We inferred ${inferredAttrs.length} ${easyPlural("attribute", inferredAttrs.length)}!
// Take a look at this schema, and if everything looks good,
// run \`push schema\` again to enforce the types.`
: 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()
: "";
: "";

// links
const linksEntries = Object.fromEntries(
Expand Down
10 changes: 0 additions & 10 deletions client/packages/core/__tests__/src/instaml.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,6 @@ test("Schema: uses info in `attrs` and `links`", () => {
},
},
},
rooms: {},
});

const commentId = uuid();
Expand Down Expand Up @@ -814,7 +813,6 @@ test("Schema: doesn't create duplicate ref attrs", () => {
},
},
},
rooms: {},
});

const commentId = uuid();
Expand Down Expand Up @@ -870,8 +868,6 @@ test("Schema: lookup creates unique attrs for custom lookups", () => {
nickname: i.string().unique().indexed(),
}),
},
links: {},
rooms: {},
});

const ops = instatx.tx.users[instatx.lookup("nickname", "stopanator")].update(
Expand Down Expand Up @@ -933,7 +929,6 @@ test("Schema: lookup creates unique attrs for lookups in link values", () => {
},
},
},
rooms: {},
});

const uid = uuid();
Expand Down Expand Up @@ -1015,7 +1010,6 @@ test("Schema: lookup creates unique attrs for lookups in link values with arrays
},
},
},
rooms: {},
});

const uid = uuid();
Expand Down Expand Up @@ -1108,7 +1102,6 @@ test("Schema: lookup creates unique ref attrs for ref lookup", () => {
},
},
},
rooms: {},
});

const uid = uuid();
Expand Down Expand Up @@ -1192,7 +1185,6 @@ test("Schema: lookup creates unique ref attrs for ref lookup in link value", ()
},
},
},
rooms: {},
});
const uid = uuid();
const ops = [
Expand Down Expand Up @@ -1257,8 +1249,6 @@ test("Schema: populates checked-data-type", () => {
j: i.json(),
}),
},
links: {},
rooms: {},
});

const commentId = uuid();
Expand Down
3 changes: 0 additions & 3 deletions client/packages/core/__tests__/src/instaql.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,6 @@ test("arbitrary ordering with dates", () => {
num: i.number().indexed(),
}),
},
links: {},
});

const txSteps = [];
Expand Down Expand Up @@ -1085,8 +1084,6 @@ test("comparators", () => {
boolean: i.boolean().indexed(),
}),
},
links: {},
rooms: {},
});

const txSteps = [];
Expand Down
2 changes: 0 additions & 2 deletions client/packages/core/__tests__/src/instaqlInference.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ test("many-to-many with inference", () => {
reverse: { on: "tags", has: "many", label: "posts" },
},
},
rooms: {},
});

const ids = {
Expand Down Expand Up @@ -94,7 +93,6 @@ test("one-to-one with inference", () => {
reverse: { on: "profiles", has: "one", label: "user" },
},
},
rooms: {},
});

const ids = {
Expand Down
1 change: 0 additions & 1 deletion client/packages/core/__tests__/src/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ test("runs without exception", () => {
},
},
},
rooms: {},
});

const demoQuery = {
Expand Down
Loading
Loading