instant-cli 1.0.40 → 1.0.41-branch-python-sdk-v1.26586025551.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ import { infoCommand } from './commands/info.ts';
21
21
  import { pullCommand } from './commands/pull.ts';
22
22
  import type { SchemaPermsOrBoth } from './commands/pull.ts';
23
23
  import { claimCommand } from './commands/claim.ts';
24
+ import { genpyCommand } from './commands/genpy.ts';
24
25
  import { pushCommand } from './commands/push.ts';
25
26
  import { explorerCmd } from './commands/explorer.ts';
26
27
  import { queryCmd } from './commands/query.ts';
@@ -836,6 +837,28 @@ Environment Variables:
836
837
  );
837
838
  });
838
839
 
840
+ export const genpyDef = program
841
+ .command('genpy')
842
+ .description(
843
+ 'Generate a schema-bound instant_types.py from instant.schema.ts.',
844
+ )
845
+ .option(
846
+ '-o --out-dir <path>',
847
+ 'Directory to write instant_types.py into. Defaults to the schema file directory.',
848
+ )
849
+ .addHelpText(
850
+ 'after',
851
+ `
852
+ Environment Variables:
853
+ INSTANT_SCHEMA_FILE_PATH Override schema file location (default: instant.schema.ts)
854
+ `,
855
+ )
856
+ .action(async (opts) => {
857
+ return runCommandEffect(
858
+ genpyCommand({ outDir: opts.outDir }).pipe(Effect.provide(BaseLayerLive)),
859
+ );
860
+ });
861
+
839
862
  export const claimDef = program
840
863
  .command('claim')
841
864
  .description('Transfer a temporary app into your Instant account')
@@ -14,18 +14,15 @@ const toArray = <T>(value: Arrayable<T>): T[] =>
14
14
  Array.isArray(value) ? value : [value];
15
15
 
16
16
  /**
17
- * Resolve @instantdb packages from CLI's dependency tree.
18
- * For Deno projects, we alias all common @instantdb packages to @instantdb/core
19
- * since they all re-export the schema types from core.
17
+ * Resolve @instantdb packages from the CLI's own dependencies for
18
+ * projects without node_modules. All @instantdb packages re-export
19
+ * schema types from core, so a single core alias covers them all.
20
20
  */
21
21
  function getInstantAliases(): Record<string, string> | null {
22
22
  try {
23
23
  const require = createRequire(import.meta.url);
24
- // Resolve @instantdb/core directly from CLI's dependencies
25
24
  const corePackageJson = require.resolve('@instantdb/core/package.json');
26
25
  const coreDir = path.dirname(corePackageJson);
27
- // All @instantdb packages re-export schema types from core,
28
- // so we can alias them all to core for schema loading purposes
29
26
  return {
30
27
  '@instantdb/core': coreDir,
31
28
  '@instantdb/react': coreDir,
@@ -39,7 +36,7 @@ function getInstantAliases(): Record<string, string> | null {
39
36
  }
40
37
  }
41
38
 
42
- function withDenoAliases<T>(
39
+ function withAliases<T>(
43
40
  opts: LoadConfigOptions<T>,
44
41
  alias: Record<string, string>,
45
42
  ): LoadConfigOptions<T> {
@@ -76,14 +73,13 @@ export async function loadConfig<T>(
76
73
  opts: LoadConfigOptions<T>,
77
74
  ): Promise<LoadConfigResult<T>> {
78
75
  const projectInfo = await findProjectDir();
79
- const isDeno = projectInfo?.type === 'deno';
76
+ const needsAliases =
77
+ projectInfo?.type === 'deno' || projectInfo?.type === 'python';
80
78
 
81
- // Deno projects don't have node_modules, so we need to alias @instantdb/*
82
- // packages to resolve from the CLI's own dependencies
83
79
  let res;
84
- if (isDeno) {
80
+ if (needsAliases) {
85
81
  const alias = getInstantAliases();
86
- res = await _loadConfig(alias ? withDenoAliases(opts, alias) : opts);
82
+ res = await _loadConfig(alias ? withAliases(opts, alias) : opts);
87
83
  } else {
88
84
  res = await _loadConfig(opts);
89
85
  }
@@ -2,32 +2,53 @@ import { packageDirectory } from 'package-directory';
2
2
  import { findUp } from 'find-up-simple';
3
3
  import path from 'node:path';
4
4
 
5
- export type ProjectType = 'node' | 'deno';
5
+ export type ProjectType = 'node' | 'deno' | 'python';
6
6
 
7
7
  export interface ProjectInfo {
8
8
  dir: string;
9
9
  type: ProjectType;
10
10
  }
11
11
 
12
+ // Same-directory tie-breaker: Deno > Python > Node. Deno may coexist
13
+ // with package.json for npm interop; pyproject.toml signals Python.
14
+ const TYPE_PRIORITY: Record<ProjectType, number> = {
15
+ deno: 0,
16
+ python: 1,
17
+ node: 2,
18
+ };
19
+
20
+ const depth = (filepath: string) => filepath.split(path.sep).length;
21
+
12
22
  export async function findProjectDir(
13
23
  cwd?: string,
14
24
  ): Promise<ProjectInfo | null> {
15
- // Check for Deno first. A Deno project may also have a package.json (for npm
16
- // compatibility), but if deno.json exists, the user intends to use Deno and
17
- // we should use Deno-specific behavior (e.g., resolving @instantdb/* from
18
- // CLI's dependencies instead of node_modules).
19
- const denoConfig =
20
- (await findUp('deno.json', { cwd })) ||
21
- (await findUp('deno.jsonc', { cwd }));
22
- if (denoConfig) {
23
- return { dir: path.dirname(denoConfig), type: 'deno' };
24
- }
25
+ const [denoJson, denoJsonc, pyproject, nodeDir] = await Promise.all([
26
+ findUp('deno.json', { cwd }),
27
+ findUp('deno.jsonc', { cwd }),
28
+ findUp('pyproject.toml', { cwd }),
29
+ packageDirectory({ cwd }),
30
+ ]);
25
31
 
26
- // Fall back to package-directory for Node
27
- const nodeDir = await packageDirectory({ cwd });
32
+ // Push deno.json and deno.jsonc independently so the depth-sort can
33
+ // pick the nearer one when both exist at different levels.
34
+ const candidates: { file: string; type: ProjectType }[] = [];
35
+ if (denoJson) candidates.push({ file: denoJson, type: 'deno' });
36
+ if (denoJsonc) candidates.push({ file: denoJsonc, type: 'deno' });
37
+ if (pyproject) candidates.push({ file: pyproject, type: 'python' });
28
38
  if (nodeDir) {
29
- return { dir: nodeDir, type: 'node' };
39
+ candidates.push({ file: path.join(nodeDir, 'package.json'), type: 'node' });
30
40
  }
31
41
 
32
- return null;
42
+ if (candidates.length === 0) return null;
43
+
44
+ // Nearest marker wins (deepest path), so a Python project nested in a
45
+ // Node monorepo isn't misclassified by a parent's package.json.
46
+ candidates.sort((a, b) => {
47
+ const depthDiff = depth(b.file) - depth(a.file);
48
+ return depthDiff !== 0
49
+ ? depthDiff
50
+ : TYPE_PRIORITY[a.type] - TYPE_PRIORITY[b.type];
51
+ });
52
+
53
+ return { dir: path.dirname(candidates[0].file), type: candidates[0].type };
33
54
  }