schemock 0.0.4-alpha.21 → 0.0.4-alpha.23

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/dist/cli/index.js CHANGED
@@ -1844,7 +1844,7 @@ function generateEntityTypes(code, schema, allSchemas) {
1844
1844
  for (const rel of relations) {
1845
1845
  if (rel.type === "hasMany") {
1846
1846
  code.line(`${rel.name}?: ${rel.targetPascal}Create[];`);
1847
- } else if (rel.type === "hasOne") {
1847
+ } else if (rel.type === "hasOne" || rel.type === "belongsTo") {
1848
1848
  code.line(`${rel.name}?: ${rel.targetPascal}Create;`);
1849
1849
  }
1850
1850
  }
@@ -3576,14 +3576,38 @@ function generateSupabaseClient(schemas, config) {
3576
3576
  code.comment("Default Supabase client (for direct access if needed)");
3577
3577
  code.line("export const supabase = createSupabaseClient(supabaseUrl, supabaseKey);");
3578
3578
  code.line();
3579
- code.block("function buildSelect(include?: string[]): string {", () => {
3579
+ const schemaMap = new Map(schemas.map((s) => [s.name, s]));
3580
+ code.block("const relationMeta: Record<string, Record<string, { type: string; target: string }>> = {", () => {
3581
+ for (const schema of schemas) {
3582
+ if (schema.isJunctionTable) continue;
3583
+ if (schema.relations.length === 0) continue;
3584
+ code.block(`${schema.name}: {`, () => {
3585
+ for (const rel of schema.relations) {
3586
+ const targetSchema = schemaMap.get(rel.resolvedTarget);
3587
+ const targetTable = config.tableMap?.[rel.resolvedTarget] ?? targetSchema?.tableName ?? rel.resolvedTarget + "s";
3588
+ code.line(`${rel.name}: { type: '${rel.type}', target: '${targetTable}' },`);
3589
+ }
3590
+ }, "},");
3591
+ }
3592
+ }, "};");
3593
+ code.line();
3594
+ code.block("function buildSelect(entityName: string, include?: string[]): string {", () => {
3580
3595
  code.line("if (!include?.length) return '*';");
3581
- code.line("return `*, ${include.map(rel => `${rel}(*)`).join(', ')}`;");
3596
+ code.line("const meta = relationMeta[entityName] || {};");
3597
+ code.line("const selectParts = include.map(rel => {");
3598
+ code.line(" const relInfo = meta[rel];");
3599
+ code.line(" if (relInfo?.type === 'belongsTo') {");
3600
+ code.line(" // belongsTo needs explicit target: 'author:users(*)'");
3601
+ code.line(" return `${rel}:${relInfo.target}(*)`;");
3602
+ code.line(" }");
3603
+ code.line(" // hasMany/hasOne can use simple syntax: 'posts(*)'");
3604
+ code.line(" return `${rel}(*)`;");
3605
+ code.line("});");
3606
+ code.line("return `*, ${selectParts.join(', ')}`;");
3582
3607
  });
3583
3608
  code.line();
3584
3609
  generateApiType2(code, schemas);
3585
3610
  code.line();
3586
- const schemaMap = new Map(schemas.map((s) => [s.name, s]));
3587
3611
  generateCreateClientFactory2(code, schemas, config, schemaMap);
3588
3612
  code.line();
3589
3613
  code.multiDocComment([
@@ -3872,7 +3896,7 @@ function generateSupabaseEntityApiFactory(code, schema, config, schemaMap) {
3872
3896
  code.indent();
3873
3897
  code.line(`executeListRequest<Types.${pascalName}>('${name}.list', async (client) => {`);
3874
3898
  code.indent();
3875
- code.line("const select = buildSelect(options?.include);");
3899
+ code.line(`const select = buildSelect('${name}', options?.include);`);
3876
3900
  code.line(`let query = client.from('${tableName}').select(select, { count: 'exact' });`);
3877
3901
  code.line();
3878
3902
  code.block("if (options?.where) {", () => {
@@ -3926,7 +3950,7 @@ function generateSupabaseEntityApiFactory(code, schema, config, schemaMap) {
3926
3950
  code.indent();
3927
3951
  code.line(`executeRequest<Types.${pascalName}>('${name}.get', async (client) => {`);
3928
3952
  code.indent();
3929
- code.line("const select = buildSelect(options?.include);");
3953
+ code.line(`const select = buildSelect('${name}', options?.include);`);
3930
3954
  code.line(`return await client.from('${tableName}').select(select).eq('id', id).single();`);
3931
3955
  code.dedent();
3932
3956
  code.line(`}).then(data => ({ data: data as Types.${pascalName} })),`);
@@ -3956,32 +3980,53 @@ function generateSupabaseEntityApiFactory(code, schema, config, schemaMap) {
3956
3980
  }
3957
3981
  function generateSupabaseCreateMethodFactory(code, schema, config, schemaMap) {
3958
3982
  const { name, pascalName, tableName, relations } = schema;
3959
- const nestedRels = relations.filter((r) => r.type === "hasMany" || r.type === "hasOne");
3983
+ const childRels = relations.filter((r) => r.type === "hasMany" || r.type === "hasOne");
3984
+ const parentRels = relations.filter((r) => r.type === "belongsTo");
3985
+ const hasNestedRels = childRels.length > 0 || parentRels.length > 0;
3960
3986
  code.line(`create: (input: Types.${pascalName}Create) =>`);
3961
3987
  code.indent();
3962
3988
  code.line(`executeRequest<Types.${pascalName}>('${name}.create', async (client) => {`);
3963
3989
  code.indent();
3964
- if (nestedRels.length > 0) {
3965
- const relNames = nestedRels.map((r) => r.name).join(", ");
3966
- code.line(`const { ${relNames}, ...data } = input;`);
3967
- code.line();
3968
- code.line(`const result = await client.from('${tableName}').insert(data).select().single();`);
3990
+ if (hasNestedRels) {
3991
+ const allRelNames = [...childRels, ...parentRels].map((r) => r.name).join(", ");
3992
+ code.line(`const { ${allRelNames}, ...data } = input;`);
3993
+ code.line("// eslint-disable-next-line @typescript-eslint/no-explicit-any");
3994
+ code.line("const insertData: Record<string, any> = { ...data };");
3995
+ code.line();
3996
+ if (parentRels.length > 0) {
3997
+ code.comment("Handle belongsTo relations - create parent first");
3998
+ for (const rel of parentRels) {
3999
+ const targetSchema = schemaMap.get(rel.resolvedTarget);
4000
+ const targetTable = config.tableMap?.[rel.resolvedTarget] ?? targetSchema?.tableName ?? rel.resolvedTarget + "s";
4001
+ const fkField = rel.localField || rel.foreignKey;
4002
+ code.block(`if (${rel.name}) {`, () => {
4003
+ code.line(`const parentResult = await client.from('${targetTable}').insert(${rel.name}).select().single();`);
4004
+ code.line("if (parentResult.error) return parentResult as { data: null; error: unknown };");
4005
+ code.line(`insertData.${fkField} = parentResult.data.id;`);
4006
+ });
4007
+ }
4008
+ code.line();
4009
+ }
4010
+ code.line(`const result = await client.from('${tableName}').insert(insertData).select().single();`);
3969
4011
  code.line("if (result.error) return result;");
3970
4012
  code.line("const item = result.data;");
3971
4013
  code.line();
3972
- for (const rel of nestedRels) {
3973
- const targetSchema = schemaMap.get(rel.target);
3974
- const targetTable = config.tableMap?.[rel.target] ?? targetSchema?.tableName ?? rel.target + "s";
3975
- code.block(`if (${rel.name}) {`, () => {
3976
- if (rel.type === "hasMany") {
3977
- code.line(`const nestedData = ${rel.name}.map(nested => ({ ...nested, ${rel.foreignKey}: item.id }));`);
3978
- code.line(`const nestedResult = await client.from('${targetTable}').insert(nestedData);`);
3979
- code.line("if (nestedResult.error) return nestedResult as { data: null; error: unknown };");
3980
- } else {
3981
- code.line(`const nestedResult = await client.from('${targetTable}').insert({ ...${rel.name}, ${rel.foreignKey}: item.id });`);
3982
- code.line("if (nestedResult.error) return nestedResult as { data: null; error: unknown };");
3983
- }
3984
- });
4014
+ if (childRels.length > 0) {
4015
+ code.comment("Handle hasMany/hasOne relations - create children after parent");
4016
+ for (const rel of childRels) {
4017
+ const targetSchema = schemaMap.get(rel.resolvedTarget);
4018
+ const targetTable = config.tableMap?.[rel.resolvedTarget] ?? targetSchema?.tableName ?? rel.resolvedTarget + "s";
4019
+ code.block(`if (${rel.name}) {`, () => {
4020
+ if (rel.type === "hasMany") {
4021
+ code.line(`const nestedData = ${rel.name}.map(nested => ({ ...nested, ${rel.foreignKey}: item.id }));`);
4022
+ code.line(`const nestedResult = await client.from('${targetTable}').insert(nestedData);`);
4023
+ code.line("if (nestedResult.error) return nestedResult as { data: null; error: unknown };");
4024
+ } else {
4025
+ code.line(`const nestedResult = await client.from('${targetTable}').insert({ ...${rel.name}, ${rel.foreignKey}: item.id });`);
4026
+ code.line("if (nestedResult.error) return nestedResult as { data: null; error: unknown };");
4027
+ }
4028
+ });
4029
+ }
3985
4030
  }
3986
4031
  code.line();
3987
4032
  code.line("return { data: item, error: null };");