@ronin/compiler 0.13.14 → 0.14.0-leo-ron-1099-1-experimental-311

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/index.d.ts CHANGED
@@ -372,8 +372,8 @@ declare class Transaction {
372
372
  statements: Array<Statement>;
373
373
  models: Array<Model>;
374
374
  constructor(queries: Array<Query>, options?: TransactionOptions);
375
- formatResults<Record>(results: Array<Array<RawRow>>, raw?: true): Array<Result<Record>>;
376
375
  formatResults<Record>(results: Array<Array<ObjectRow>>, raw?: false): Array<Result<Record>>;
376
+ formatResults<Record>(results: Array<Array<RawRow>>, raw?: true): Array<Result<Record>>;
377
377
  }
378
378
 
379
379
  declare const CLEAN_ROOT_MODEL: PublicModel;
package/dist/index.js CHANGED
@@ -125,7 +125,6 @@ var getProperty = (obj, path) => {
125
125
  return path.split(".").reduce((acc, key) => acc?.[key], obj);
126
126
  };
127
127
  var setProperty = (obj, path, value) => {
128
- if (!obj) return setProperty({}, path, value);
129
128
  const segments = path.split(/[.[\]]/g).filter((x) => !!x.trim());
130
129
  const _set = (node) => {
131
130
  if (segments.length > 1) {
@@ -139,9 +138,7 @@ var setProperty = (obj, path, value) => {
139
138
  node[segments[0]] = value;
140
139
  }
141
140
  };
142
- const cloned = structuredClone(obj);
143
- _set(cloned);
144
- return cloned;
141
+ _set(obj);
145
142
  };
146
143
  var splitQuery = (query) => {
147
144
  const queryType = Object.keys(query)[0];
@@ -302,7 +299,7 @@ var handleFor = (model, instructions) => {
302
299
  };
303
300
 
304
301
  // src/instructions/including.ts
305
- var handleIncluding = (models, model, statementParams, instruction) => {
302
+ var handleIncluding = (models, model, statementParams, single, instruction) => {
306
303
  let statement = "";
307
304
  let tableSubQuery;
308
305
  for (const ephemeralFieldSlug in instruction) {
@@ -315,10 +312,10 @@ var handleIncluding = (models, model, statementParams, instruction) => {
315
312
  let joinType = "LEFT";
316
313
  let relatedTableSelector = `"${relatedModel.table}"`;
317
314
  const tableAlias = composeIncludedTableAlias(ephemeralFieldSlug);
318
- const single = queryModel !== relatedModel.pluralSlug;
315
+ const subSingle = queryModel !== relatedModel.pluralSlug;
319
316
  if (!modifiableQueryInstructions?.with) {
320
317
  joinType = "CROSS";
321
- if (single) {
318
+ if (subSingle) {
322
319
  if (!modifiableQueryInstructions) modifiableQueryInstructions = {};
323
320
  modifiableQueryInstructions.limitedTo = 1;
324
321
  }
@@ -350,7 +347,19 @@ var handleIncluding = (models, model, statementParams, instruction) => {
350
347
  );
351
348
  statement += ` ON (${subStatement})`;
352
349
  }
353
- if (!single) tableSubQuery = `SELECT * FROM "${model.table}" LIMIT 1`;
350
+ if (single && !subSingle) {
351
+ tableSubQuery = `SELECT * FROM "${model.table}" LIMIT 1`;
352
+ }
353
+ if (modifiableQueryInstructions?.including) {
354
+ const subIncluding = handleIncluding(
355
+ models,
356
+ { ...relatedModel, tableAlias },
357
+ statementParams,
358
+ subSingle,
359
+ modifiableQueryInstructions.including
360
+ );
361
+ statement += ` ${subIncluding.statement}`;
362
+ }
354
363
  }
355
364
  return { statement, tableSubQuery };
356
365
  };
@@ -392,11 +401,23 @@ var handleOrderedBy = (model, instruction) => {
392
401
  };
393
402
 
394
403
  // src/instructions/selecting.ts
395
- var handleSelecting = (models, model, statementParams, instructions, options) => {
396
- let loadedFields = [];
397
- let expandColumns = false;
398
- let statement = "*";
404
+ var handleSelecting = (models, model, statementParams, single, instructions, options = {}) => {
399
405
  let isJoining = false;
406
+ const selectedFields = (instructions.selecting ? instructions.selecting.map((slug) => {
407
+ const { field } = getFieldFromModel(model, slug, {
408
+ instructionName: "selecting"
409
+ });
410
+ return field;
411
+ }) : model.fields).filter((field) => !(field.type === "link" && field.kind === "many")).map((field) => {
412
+ const newField = { ...field, mountingPath: field.slug };
413
+ if (options.mountingPath) {
414
+ newField.mountingPath = `${options.mountingPath}.${field.slug}`;
415
+ }
416
+ return newField;
417
+ });
418
+ if (instructions.selecting) options.expandColumns = true;
419
+ const joinedSelectedFields = [];
420
+ const joinedColumns = [];
400
421
  if (instructions.including) {
401
422
  const flatObject = flatten(instructions.including);
402
423
  instructions.including = {};
@@ -406,73 +427,65 @@ var handleSelecting = (models, model, statementParams, instructions, options) =>
406
427
  const { queryModel, queryInstructions } = splitQuery(symbol.value);
407
428
  const subQueryModel = getModelBySlug(models, queryModel);
408
429
  isJoining = true;
409
- expandColumns = Boolean(options?.expandColumns || queryInstructions?.selecting);
430
+ if (queryInstructions?.selecting) options.expandColumns = true;
410
431
  const tableAlias = composeIncludedTableAlias(key);
411
- const single = queryModel !== subQueryModel.pluralSlug;
412
- if (!single) {
413
- model.tableAlias = `sub_${model.table}`;
414
- }
415
- const queryModelFields = queryInstructions?.selecting ? subQueryModel.fields.filter((field) => {
416
- return queryInstructions.selecting?.includes(field.slug);
417
- }) : (
418
- // Exclude link fields with cardinality "many", since those don't exist as columns.
419
- subQueryModel.fields.filter((field) => {
420
- return !(field.type === "link" && field.kind === "many");
421
- })
432
+ const subSingle = queryModel !== subQueryModel.pluralSlug;
433
+ if (!model.tableAlias)
434
+ model.tableAlias = single && !subSingle ? `sub_${model.table}` : model.table;
435
+ const subMountingPath = `${options?.mountingPath ? `${options?.mountingPath}.` : ""}${subSingle ? key : `${key}[0]`}`;
436
+ const { columns: nestedColumns, selectedFields: nestedSelectedFields } = handleSelecting(
437
+ models,
438
+ { ...subQueryModel, tableAlias },
439
+ statementParams,
440
+ subSingle,
441
+ {
442
+ selecting: queryInstructions?.selecting,
443
+ including: queryInstructions?.including
444
+ },
445
+ { ...options, mountingPath: subMountingPath }
422
446
  );
423
- for (const field of queryModelFields) {
424
- loadedFields.push({ ...field, parentField: key });
425
- if (expandColumns) {
426
- const newValue2 = parseFieldExpression(
427
- { ...subQueryModel, tableAlias },
428
- "including",
429
- `${QUERY_SYMBOLS.FIELD}${field.slug}`
430
- );
431
- instructions.including[`${tableAlias}.${field.slug}`] = newValue2;
432
- }
433
- }
447
+ if (nestedColumns !== "*") joinedColumns.push(nestedColumns);
448
+ joinedSelectedFields.push(...nestedSelectedFields);
434
449
  continue;
435
450
  }
436
- let newValue = value;
451
+ let mountedValue = value;
437
452
  if (symbol?.type === "expression") {
438
- newValue = `(${parseFieldExpression(model, "including", symbol.value)})`;
453
+ mountedValue = `(${parseFieldExpression(model, "including", symbol.value)})`;
439
454
  } else {
440
- newValue = prepareStatementValue(statementParams, value);
455
+ mountedValue = prepareStatementValue(statementParams, value);
441
456
  }
442
- instructions.including[key] = newValue;
443
- loadedFields.push({
457
+ selectedFields.push({
444
458
  slug: key,
445
- type: RAW_FIELD_TYPES.includes(typeof value) ? typeof value : "string"
459
+ mountingPath: key,
460
+ type: RAW_FIELD_TYPES.includes(typeof value) ? typeof value : "string",
461
+ mountedValue
446
462
  });
447
463
  }
448
464
  }
449
- if (expandColumns) {
450
- instructions.selecting = model.fields.filter((field) => !(field.type === "link" && field.kind === "many")).map((field) => field.slug);
451
- }
452
- if (instructions.selecting) {
453
- const usableModel = expandColumns ? { ...model, tableAlias: model.tableAlias || model.table } : model;
454
- const selectedFields = [];
455
- statement = instructions.selecting.map((slug) => {
456
- const { field, fieldSelector } = getFieldFromModel(usableModel, slug, {
457
- instructionName: "selecting"
458
- });
459
- selectedFields.push(field);
460
- return fieldSelector;
461
- }).join(", ");
462
- loadedFields = [...selectedFields, ...loadedFields];
463
- } else {
464
- loadedFields = [
465
- ...model.fields.filter(
466
- (field) => !(field.type === "link" && field.kind === "many")
467
- ),
468
- ...loadedFields
469
- ];
470
- }
471
- if (instructions.including && Object.keys(instructions.including).length > 0) {
472
- statement += ", ";
473
- statement += Object.entries(instructions.including).map(([key, value]) => `${value} as "${key}"`).join(", ");
474
- }
475
- return { columns: statement, isJoining, loadedFields };
465
+ let columns = ["*"];
466
+ const fieldsToExpand = options.expandColumns ? selectedFields : selectedFields.filter(
467
+ (loadedField) => typeof loadedField.mountedValue !== "undefined"
468
+ );
469
+ const extraColumns = fieldsToExpand.map((selectedField) => {
470
+ if (selectedField.mountedValue) {
471
+ return `${selectedField.mountedValue} as "${selectedField.slug}"`;
472
+ }
473
+ const { fieldSelector } = getFieldFromModel(model, selectedField.slug, {
474
+ instructionName: "selecting"
475
+ });
476
+ if (model.tableAlias?.startsWith("including_")) {
477
+ return `${fieldSelector} as "${model.tableAlias}.${selectedField.slug}"`;
478
+ }
479
+ return fieldSelector;
480
+ });
481
+ if (options.expandColumns) {
482
+ columns = extraColumns;
483
+ } else if (extraColumns) {
484
+ columns.push(...extraColumns);
485
+ }
486
+ columns.push(...joinedColumns);
487
+ selectedFields.push(...joinedSelectedFields);
488
+ return { columns: columns.join(", "), isJoining, selectedFields };
476
489
  };
477
490
 
478
491
  // src/instructions/to.ts
@@ -594,7 +607,7 @@ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
594
607
  defaultQuery
595
608
  );
596
609
  if (query === null)
597
- return { dependencies: [], main: dependencyStatements[0], loadedFields: [] };
610
+ return { dependencies: [], main: dependencyStatements[0], selectedFields: [] };
598
611
  const parsedQuery = splitQuery(query);
599
612
  const { queryType, queryModel, queryInstructions } = parsedQuery;
600
613
  const model = getModelBySlug(models, queryModel);
@@ -604,10 +617,11 @@ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
604
617
  if (instructions && Object.hasOwn(instructions, "for")) {
605
618
  instructions = handleFor(model, instructions);
606
619
  }
607
- const { columns, isJoining, loadedFields } = handleSelecting(
620
+ const { columns, isJoining, selectedFields } = handleSelecting(
608
621
  models,
609
622
  model,
610
623
  statementParams,
624
+ single,
611
625
  {
612
626
  selecting: instructions?.selecting,
613
627
  including: instructions?.including
@@ -638,6 +652,7 @@ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
638
652
  models,
639
653
  model,
640
654
  statementParams,
655
+ single,
641
656
  instructions?.including
642
657
  );
643
658
  if (tableSubQuery) {
@@ -734,7 +749,7 @@ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
734
749
  return {
735
750
  dependencies: dependencyStatements,
736
751
  main: mainStatement,
737
- loadedFields
752
+ selectedFields
738
753
  };
739
754
  };
740
755
 
@@ -1292,8 +1307,10 @@ var getFieldSelector = (model, field, fieldPath, writing) => {
1292
1307
  if (field.type === "json" && !writing) {
1293
1308
  const dotParts = fieldPath.split(".");
1294
1309
  const columnName = tablePrefix + dotParts.shift();
1295
- const jsonField = dotParts.join(".");
1296
- return `json_extract(${columnName}, '$.${jsonField}')`;
1310
+ if (dotParts.length > 0) {
1311
+ const jsonField = dotParts.join(".");
1312
+ return `json_extract(${columnName}, '$.${jsonField}')`;
1313
+ }
1297
1314
  }
1298
1315
  return `${tablePrefix}"${fieldPath}"`;
1299
1316
  };
@@ -1894,7 +1911,7 @@ var Transaction = class {
1894
1911
  ...subStatements.map((statement) => ({
1895
1912
  ...statement,
1896
1913
  query,
1897
- fields: result.loadedFields
1914
+ selectedFields: result.selectedFields
1898
1915
  }))
1899
1916
  );
1900
1917
  }
@@ -1903,38 +1920,40 @@ var Transaction = class {
1903
1920
  };
1904
1921
  #formatRows(fields, rows, single, isMeta) {
1905
1922
  const records = [];
1906
- for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
1907
- const row = rows[rowIndex];
1908
- for (let valueIndex = 0; valueIndex < row.length; valueIndex++) {
1909
- const value = row[valueIndex];
1910
- const field = fields[valueIndex];
1911
- let newSlug = field.slug;
1912
- let newValue = value;
1923
+ for (const row of rows) {
1924
+ const record = fields.reduce((acc, field, fieldIndex) => {
1925
+ const newSlug = field.mountingPath;
1926
+ let newValue = row[fieldIndex];
1913
1927
  if (field.type === "json") {
1914
- newValue = JSON.parse(value);
1928
+ newValue = JSON.parse(newValue);
1915
1929
  } else if (field.type === "boolean") {
1916
- newValue = Boolean(value);
1917
- }
1918
- const parentFieldSlug = field.parentField;
1919
- let usableRowIndex = rowIndex;
1920
- if (parentFieldSlug) {
1921
- if (rows.length === 1) {
1922
- newSlug = `${parentFieldSlug}.${field.slug}`;
1923
- } else {
1924
- newSlug = `${parentFieldSlug}[${rowIndex}].${field.slug}`;
1925
- usableRowIndex = 0;
1926
- }
1930
+ newValue = Boolean(newValue);
1927
1931
  }
1928
1932
  if (isMeta && PLURAL_MODEL_ENTITIES_VALUES.includes(newSlug)) {
1929
1933
  newValue = newValue ? Object.entries(newValue).map(([slug, attributes]) => {
1930
1934
  return { slug, ...attributes };
1931
1935
  }) : [];
1932
1936
  }
1933
- records[usableRowIndex] = setProperty(
1934
- records[usableRowIndex],
1935
- newSlug,
1936
- newValue
1937
- );
1937
+ setProperty(acc, newSlug, newValue);
1938
+ return acc;
1939
+ }, {});
1940
+ const existingRecord = record.id ? records.find((existingRecord2) => {
1941
+ return existingRecord2.id === record.id;
1942
+ }) : null;
1943
+ if (!existingRecord) {
1944
+ records.push(record);
1945
+ continue;
1946
+ }
1947
+ const joinFields = fields.reduce(
1948
+ (acc, { mountingPath }) => {
1949
+ return mountingPath.includes("[0]") && !acc.includes(mountingPath.split("[0]")[0]) ? acc.concat([mountingPath.split("[0]")[0]]) : acc;
1950
+ },
1951
+ []
1952
+ );
1953
+ for (const arrayField of joinFields) {
1954
+ const currentValue = existingRecord[arrayField];
1955
+ const newValue = record[arrayField];
1956
+ currentValue.push(...newValue);
1938
1957
  }
1939
1958
  }
1940
1959
  return single ? records[0] : records;
@@ -1961,11 +1980,7 @@ var Transaction = class {
1961
1980
  });
1962
1981
  const formattedResults = normalizedResults.map(
1963
1982
  (rows, index) => {
1964
- const {
1965
- returning,
1966
- query,
1967
- fields: rawModelFields
1968
- } = this.#internalStatements[index];
1983
+ const { returning, query, selectedFields } = this.#internalStatements[index];
1969
1984
  if (!returning) return null;
1970
1985
  const { queryType, queryModel, queryInstructions } = splitQuery(query);
1971
1986
  const model = getModelBySlug(this.models, queryModel);
@@ -1979,13 +1994,13 @@ var Transaction = class {
1979
1994
  const single = queryModel !== model.pluralSlug;
1980
1995
  if (single) {
1981
1996
  return {
1982
- record: rows[0] ? this.#formatRows(rawModelFields, rows, single, isMeta) : null,
1997
+ record: rows[0] ? this.#formatRows(selectedFields, rows, true, isMeta) : null,
1983
1998
  modelFields
1984
1999
  };
1985
2000
  }
1986
2001
  const pageSize = queryInstructions?.limitedTo;
1987
2002
  const output = {
1988
- records: this.#formatRows(rawModelFields, rows, single, isMeta),
2003
+ records: this.#formatRows(selectedFields, rows, false, isMeta),
1989
2004
  modelFields
1990
2005
  };
1991
2006
  if (pageSize && output.records.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ronin/compiler",
3
- "version": "0.13.14",
3
+ "version": "0.14.0-leo-ron-1099-1-experimental-311",
4
4
  "type": "module",
5
5
  "description": "Compiles RONIN queries to SQL statements.",
6
6
  "publishConfig": {