@apso/cli 0.1.9 → 0.3.0

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.
Files changed (45) hide show
  1. package/README.md +511 -0
  2. package/dist/commands/server/scaffold.js +1 -1
  3. package/dist/lib/controller.js +2 -2
  4. package/dist/lib/dto.js +1 -1
  5. package/dist/lib/entity.js +1 -1
  6. package/dist/lib/service.d.ts +1 -8
  7. package/dist/lib/service.js +28 -4
  8. package/dist/lib/templates/entities/entity-col-datetime.eta +4 -0
  9. package/dist/lib/templates/entities/entity-col-geography.eta +29 -0
  10. package/dist/lib/templates/entities/entity-col-geometry.eta +29 -0
  11. package/dist/lib/templates/entities/entity-col-geometrycollection.eta +29 -0
  12. package/dist/lib/templates/entities/entity-col-linestring.eta +37 -0
  13. package/dist/lib/templates/entities/entity-col-multilinestring.eta +44 -0
  14. package/dist/lib/templates/entities/entity-col-multipoint.eta +37 -0
  15. package/dist/lib/templates/entities/entity-col-multipolygon.eta +50 -0
  16. package/dist/lib/templates/entities/entity-col-polygon.eta +44 -0
  17. package/dist/lib/templates/graphql/dto/dto-col-decimal.eta +4 -0
  18. package/dist/lib/templates/graphql/dto/dto-col-geography.eta +4 -0
  19. package/dist/lib/templates/graphql/dto/dto-col-geometry.eta +4 -0
  20. package/dist/lib/templates/graphql/dto/dto-col-geometrycollection.eta +4 -0
  21. package/dist/lib/templates/graphql/dto/dto-col-linestring.eta +4 -0
  22. package/dist/lib/templates/graphql/dto/dto-col-multilinestring.eta +4 -0
  23. package/dist/lib/templates/graphql/dto/dto-col-multipoint.eta +4 -0
  24. package/dist/lib/templates/graphql/dto/dto-col-multipolygon.eta +4 -0
  25. package/dist/lib/templates/graphql/dto/dto-col-numeric.eta +4 -0
  26. package/dist/lib/templates/graphql/dto/dto-col-point.eta +4 -0
  27. package/dist/lib/templates/graphql/dto/dto-col-polygon.eta +4 -0
  28. package/dist/lib/templates/header.eta +8 -3
  29. package/dist/lib/templates/rest/dto-rest.eta +26 -1
  30. package/dist/lib/templates/rest/ormconfig.eta +2 -0
  31. package/dist/lib/templates/rest/service-rest-spec.eta +16 -7
  32. package/dist/lib/types/field.d.ts +3 -1
  33. package/dist/lib/utils/field.d.ts +1 -0
  34. package/dist/lib/utils/field.js +42 -9
  35. package/dist/lib/utils/file-system.d.ts +4 -0
  36. package/dist/lib/utils/file-system.js +9 -1
  37. package/dist/lib/utils/relationships/parse.js +1 -1
  38. package/dist/tests/templates/entities/types/decimal-field-validation.spec.d.ts +1 -0
  39. package/dist/tests/templates/entities/types/decimal-field-validation.spec.js +100 -0
  40. package/dist/tests/templates/entities/types/geometry-template.spec.d.ts +1 -0
  41. package/dist/tests/templates/entities/types/geometry-template.spec.js +58 -0
  42. package/dist/tests/templates/entities/types/polygon-template.spec.d.ts +1 -0
  43. package/dist/tests/templates/entities/types/polygon-template.spec.js +58 -0
  44. package/oclif.manifest.json +1 -1
  45. package/package.json +1 -1
@@ -0,0 +1,29 @@
1
+ <% const {field} = it; %>
2
+
3
+ @IsOptional({ groups: [UPDATE] })
4
+ <% if (!field.nullable && !field.auto) { %>
5
+ @IsNotEmpty({ groups: [CREATE] })
6
+ <% } %>
7
+
8
+ @Column({
9
+ "type": "geometry",
10
+ transformer: {
11
+ to: (geometry: any) => {
12
+ if (!geometry) return null;
13
+ return geometry;
14
+ },
15
+ from: (pgGeometry: any) => {
16
+ if (!pgGeometry) return null;
17
+ return pgGeometry;
18
+ }
19
+ }<% if (field.nullable) {%>,
20
+ nullable: true<% } %>
21
+ })
22
+ <% if (field.index ) {%>
23
+ @Index()
24
+ <% } %>
25
+ <% if (field.primary ) {%>
26
+ @PrimaryColumn()
27
+ <% } %>
28
+
29
+ <%= field.name %>: any;
@@ -0,0 +1,29 @@
1
+ <% const {field} = it; %>
2
+
3
+ @IsOptional({ groups: [UPDATE] })
4
+ <% if (!field.nullable && !field.auto) { %>
5
+ @IsNotEmpty({ groups: [CREATE] })
6
+ <% } %>
7
+
8
+ @Column({
9
+ "type": "geometrycollection",
10
+ transformer: {
11
+ to: (geometrycollection: { geometries: Array<any> } | null) => {
12
+ if (!geometrycollection) return null;
13
+ return geometrycollection;
14
+ },
15
+ from: (pgGeometryCollection: any) => {
16
+ if (!pgGeometryCollection) return null;
17
+ return pgGeometryCollection;
18
+ }
19
+ }<% if (field.nullable) {%>,
20
+ nullable: true<% } %>
21
+ })
22
+ <% if (field.index ) {%>
23
+ @Index()
24
+ <% } %>
25
+ <% if (field.primary ) {%>
26
+ @PrimaryColumn()
27
+ <% } %>
28
+
29
+ <%= field.name %>: { geometries: Array<any> };
@@ -0,0 +1,37 @@
1
+ <% const {field} = it; %>
2
+
3
+ @IsOptional({ groups: [UPDATE] })
4
+ <% if (!field.nullable && !field.auto) { %>
5
+ @IsNotEmpty({ groups: [CREATE] })
6
+ <% } %>
7
+
8
+ @Column({
9
+ "type": "linestring",
10
+ transformer: {
11
+ to: (linestring: { coordinates: Array<{x: number, y: number}> } | null) => {
12
+ if (!linestring) return null;
13
+ const coords = linestring.coordinates.map(coord => `${coord.x} ${coord.y}`).join(',');
14
+ return `LINESTRING(${coords})`;
15
+ },
16
+ from: (pgLinestring: string | null) => {
17
+ if (!pgLinestring) return null;
18
+ // Extract coordinates from LINESTRING(x1 y1,x2 y2,...) format
19
+ const match = pgLinestring.match(/LINESTRING\((.+)\)/);
20
+ if (!match) return null;
21
+ const coords = match[1].split(',').map(coord => {
22
+ const [x, y] = coord.trim().split(' ');
23
+ return { x: parseFloat(x), y: parseFloat(y) };
24
+ });
25
+ return { coordinates: coords };
26
+ }
27
+ }<% if (field.nullable) {%>,
28
+ nullable: true<% } %>
29
+ })
30
+ <% if (field.index ) {%>
31
+ @Index()
32
+ <% } %>
33
+ <% if (field.primary ) {%>
34
+ @PrimaryColumn()
35
+ <% } %>
36
+
37
+ <%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
@@ -0,0 +1,44 @@
1
+ <% const {field} = it; %>
2
+
3
+ @IsOptional({ groups: [UPDATE] })
4
+ <% if (!field.nullable && !field.auto) { %>
5
+ @IsNotEmpty({ groups: [CREATE] })
6
+ <% } %>
7
+
8
+ @Column({
9
+ "type": "multilinestring",
10
+ transformer: {
11
+ to: (multilinestring: { coordinates: Array<Array<{x: number, y: number}>> } | null) => {
12
+ if (!multilinestring) return null;
13
+ const linestrings = multilinestring.coordinates.map(linestring => {
14
+ const coords = linestring.map(coord => `${coord.x} ${coord.y}`).join(',');
15
+ return `(${coords})`;
16
+ });
17
+ return `MULTILINESTRING(${linestrings.join(',')})`;
18
+ },
19
+ from: (pgMultilinestring: string | null) => {
20
+ if (!pgMultilinestring) return null;
21
+ // Extract coordinates from MULTILINESTRING((x1 y1,x2 y2,...),(x1 y1,x2 y2,...)) format
22
+ const match = pgMultilinestring.match(/MULTILINESTRING\((.+)\)/);
23
+ if (!match) return null;
24
+ const linestrings = match[1].split('),(').map(linestring => {
25
+ const cleanLinestring = linestring.replace(/[()]/g, '');
26
+ const coords = cleanLinestring.split(',').map(coord => {
27
+ const [x, y] = coord.trim().split(' ');
28
+ return { x: parseFloat(x), y: parseFloat(y) };
29
+ });
30
+ return coords;
31
+ });
32
+ return { coordinates: linestrings };
33
+ }
34
+ }<% if (field.nullable) {%>,
35
+ nullable: true<% } %>
36
+ })
37
+ <% if (field.index ) {%>
38
+ @Index()
39
+ <% } %>
40
+ <% if (field.primary ) {%>
41
+ @PrimaryColumn()
42
+ <% } %>
43
+
44
+ <%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
@@ -0,0 +1,37 @@
1
+ <% const {field} = it; %>
2
+
3
+ @IsOptional({ groups: [UPDATE] })
4
+ <% if (!field.nullable && !field.auto) { %>
5
+ @IsNotEmpty({ groups: [CREATE] })
6
+ <% } %>
7
+
8
+ @Column({
9
+ "type": "multipoint",
10
+ transformer: {
11
+ to: (multipoint: { coordinates: Array<{x: number, y: number}> } | null) => {
12
+ if (!multipoint) return null;
13
+ const coords = multipoint.coordinates.map(coord => `${coord.x} ${coord.y}`).join(',');
14
+ return `MULTIPOINT(${coords})`;
15
+ },
16
+ from: (pgMultipoint: string | null) => {
17
+ if (!pgMultipoint) return null;
18
+ // Extract coordinates from MULTIPOINT(x1 y1,x2 y2,...) format
19
+ const match = pgMultipoint.match(/MULTIPOINT\((.+)\)/);
20
+ if (!match) return null;
21
+ const coords = match[1].split(',').map(coord => {
22
+ const [x, y] = coord.trim().split(' ');
23
+ return { x: parseFloat(x), y: parseFloat(y) };
24
+ });
25
+ return { coordinates: coords };
26
+ }
27
+ }<% if (field.nullable) {%>,
28
+ nullable: true<% } %>
29
+ })
30
+ <% if (field.index ) {%>
31
+ @Index()
32
+ <% } %>
33
+ <% if (field.primary ) {%>
34
+ @PrimaryColumn()
35
+ <% } %>
36
+
37
+ <%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
@@ -0,0 +1,50 @@
1
+ <% const {field} = it; %>
2
+
3
+ @IsOptional({ groups: [UPDATE] })
4
+ <% if (!field.nullable && !field.auto) { %>
5
+ @IsNotEmpty({ groups: [CREATE] })
6
+ <% } %>
7
+
8
+ @Column({
9
+ "type": "multipolygon",
10
+ transformer: {
11
+ to: (multipolygon: { coordinates: Array<Array<Array<{x: number, y: number}>>> } | null) => {
12
+ if (!multipolygon) return null;
13
+ const polygons = multipolygon.coordinates.map(polygon => {
14
+ const rings = polygon.map(ring => {
15
+ const coords = ring.map(coord => `${coord.x} ${coord.y}`).join(',');
16
+ return `(${coords})`;
17
+ });
18
+ return `(${rings.join(',')})`;
19
+ });
20
+ return `MULTIPOLYGON(${polygons.join(',')})`;
21
+ },
22
+ from: (pgMultipolygon: string | null) => {
23
+ if (!pgMultipolygon) return null;
24
+ // Extract coordinates from MULTIPOLYGON(((x1 y1,x2 y2,...),(x1 y1,x2 y2,...)),((x1 y1,x2 y2,...),(x1 y1,x2 y2,...))) format
25
+ const match = pgMultipolygon.match(/MULTIPOLYGON\((.+)\)/);
26
+ if (!match) return null;
27
+ const polygons = match[1].split(')),((').map(polygon => {
28
+ const cleanPolygon = polygon.replace(/[()]/g, '');
29
+ const rings = cleanPolygon.split('),(').map(ring => {
30
+ const coords = ring.split(',').map(coord => {
31
+ const [x, y] = coord.trim().split(' ');
32
+ return { x: parseFloat(x), y: parseFloat(y) };
33
+ });
34
+ return coords;
35
+ });
36
+ return rings;
37
+ });
38
+ return { coordinates: polygons };
39
+ }
40
+ }<% if (field.nullable) {%>,
41
+ nullable: true<% } %>
42
+ })
43
+ <% if (field.index ) {%>
44
+ @Index()
45
+ <% } %>
46
+ <% if (field.primary ) {%>
47
+ @PrimaryColumn()
48
+ <% } %>
49
+
50
+ <%= field.name %>: { coordinates: Array<Array<Array<{ x: number, y: number }>>> };
@@ -0,0 +1,44 @@
1
+ <% const {field} = it; %>
2
+
3
+ @IsOptional({ groups: [UPDATE] })
4
+ <% if (!field.nullable && !field.auto) { %>
5
+ @IsNotEmpty({ groups: [CREATE] })
6
+ <% } %>
7
+
8
+ @Column({
9
+ "type": "polygon",
10
+ transformer: {
11
+ to: (polygon: { coordinates: Array<Array<{x: number, y: number}>> } | null) => {
12
+ if (!polygon) return null;
13
+ const rings = polygon.coordinates.map(ring => {
14
+ const coords = ring.map(coord => `${coord.x} ${coord.y}`).join(',');
15
+ return `(${coords})`;
16
+ });
17
+ return `POLYGON(${rings.join(',')})`;
18
+ },
19
+ from: (pgPolygon: string | null) => {
20
+ if (!pgPolygon) return null;
21
+ // Extract coordinates from POLYGON((x1 y1,x2 y2,...),(x1 y1,x2 y2,...)) format
22
+ const match = pgPolygon.match(/POLYGON\((.+)\)/);
23
+ if (!match) return null;
24
+ const rings = match[1].split('),(').map(ring => {
25
+ const cleanRing = ring.replace(/[()]/g, '');
26
+ const coords = cleanRing.split(',').map(coord => {
27
+ const [x, y] = coord.trim().split(' ');
28
+ return { x: parseFloat(x), y: parseFloat(y) };
29
+ });
30
+ return coords;
31
+ });
32
+ return { coordinates: rings };
33
+ }
34
+ }<% if (field.nullable) {%>,
35
+ nullable: true<% } %>
36
+ })
37
+ <% if (field.index ) {%>
38
+ @Index()
39
+ <% } %>
40
+ <% if (field.primary ) {%>
41
+ @PrimaryColumn()
42
+ <% } %>
43
+
44
+ <%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
@@ -0,0 +1,4 @@
1
+ <% const {field} = it; %>
2
+
3
+ @Field(() => Number <% if (field.default !== null) {%>, { nullable: true } <% }%>)
4
+ <%= field.name %>: <%= field.dataType %>;
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: any;
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: any;
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: { geometries: Array<any> };
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: { coordinates: Array<Array<Array<{ x: number, y: number }>>> };
@@ -0,0 +1,4 @@
1
+ <% const {field} = it; %>
2
+
3
+ @Field(() => Number <% if (field.default !== null) {%>, { nullable: true } <% }%>)
4
+ <%= field.name %>: <%= field.dataType %>;
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: { x: number, y: number };
@@ -0,0 +1,4 @@
1
+ <% const {field, isGql} = it; %>
2
+
3
+ @Field(() => JSONResolver, { nullable: true })
4
+ <%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
@@ -1,3 +1,8 @@
1
- /**
2
- * This file generated by Apso.
3
- */
1
+ <% /*
2
+ * This file is generated by Apso.
3
+ *
4
+ * Generated at: <%= it.generatedAt %>
5
+ * Generated by: <%= it.generatedBy %>
6
+ *
7
+ * Do not edit this file directly.
8
+ */ %>
@@ -1,6 +1,7 @@
1
- import { ApiProperty } from '@nestjs/swagger';
2
1
  <%~ includeFile('./header.eta') %>
3
2
 
3
+ import { ApiProperty } from '@nestjs/swagger';
4
+
4
5
  <% if (it.importEnums) { %>
5
6
  import * as enums from '../../enums';
6
7
  <% } %>
@@ -14,6 +15,18 @@ export class <%= it.entityName %>Create {
14
15
  <%= field.name %>: JSON;
15
16
  <%} else if (field.type === 'timestamp' || field.type === 'timestamptz') {%>
16
17
  <%= field.name %>: Date;
18
+ <%} else if (field.type === 'geometry' || field.type === 'geography') {%>
19
+ <%= field.name %>: any;
20
+ <%} else if (field.type === 'point') {%>
21
+ <%= field.name %>: { x: number, y: number };
22
+ <%} else if (field.type === 'linestring' || field.type === 'multipoint') {%>
23
+ <%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
24
+ <%} else if (field.type === 'polygon' || field.type === 'multilinestring') {%>
25
+ <%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
26
+ <%} else if (field.type === 'multipolygon') {%>
27
+ <%= field.name %>: { coordinates: Array<Array<Array<{ x: number, y: number }>>> };
28
+ <%} else if (field.type === 'geometrycollection') {%>
29
+ <%= field.name %>: { geometries: Array<any> };
17
30
  <%} else {%>
18
31
  <%= field.name %>: <%= field.dataType %>;
19
32
  <% } %>
@@ -33,6 +46,18 @@ export class <%= it.entityName %> {
33
46
  <%= field.name %>: JSON;
34
47
  <%} else if (field.type === 'timestamp' || field.type === 'timestamptz') {%>
35
48
  <%= field.name %>: Date;
49
+ <%} else if (field.type === 'geometry' || field.type === 'geography') {%>
50
+ <%= field.name %>: any;
51
+ <%} else if (field.type === 'point') {%>
52
+ <%= field.name %>: { x: number, y: number };
53
+ <%} else if (field.type === 'linestring' || field.type === 'multipoint') {%>
54
+ <%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
55
+ <%} else if (field.type === 'polygon' || field.type === 'multilinestring') {%>
56
+ <%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
57
+ <%} else if (field.type === 'multipolygon') {%>
58
+ <%= field.name %>: { coordinates: Array<Array<Array<{ x: number, y: number }>>> };
59
+ <%} else if (field.type === 'geometrycollection') {%>
60
+ <%= field.name %>: { geometries: Array<any> };
36
61
  <%} else {%>
37
62
  <%= field.name %>: <%= field.dataType %>;
38
63
  <% } %>
@@ -1,3 +1,5 @@
1
+ <%~ includeFile('./header.eta') %>
2
+
1
3
  <% it.entities.forEach((entity) => { %>
2
4
  import {<%= entity.name %>} from './autogen/<%= entity.name %>/<%= entity.name %>.entity'
3
5
  <%}) %>
@@ -1,7 +1,14 @@
1
+ <%~ includeFile('./header.eta') %>
2
+
1
3
  import { Test, TestingModule } from '@nestjs/testing';
2
4
  import { <%= it.svcName %> } from './<%= it.entityName %>.service';
3
- import { getRepositoryToken } from '@nestjs/typeorm';
5
+ import { TypeOrmModule } from '@nestjs/typeorm';
4
6
  import { <%= it.entityName %> } from './<%= it.entityName %>.entity';
7
+ <% it.allRelatedEntities.forEach(function(entity) { if (entity !== it.entityName) { %>
8
+ import { <%= entity %> } from '../<%= entity %>/<%= entity %>.entity';
9
+ <% }}); %>
10
+
11
+ // All related entities (including the main entity) are included below to ensure TypeORM can resolve relationships for this service's entity and its graph.
5
12
 
6
13
  describe('<%= it.svcName %>', () => {
7
14
  let service: <%= it.svcName %>;
@@ -18,13 +25,15 @@ describe('<%= it.svcName %>', () => {
18
25
  };
19
26
 
20
27
  const module: TestingModule = await Test.createTestingModule({
21
- providers: [
22
- <%= it.svcName %>,
23
- {
24
- provide: getRepositoryToken(<%= it.entityName %>),
25
- useValue: mockRepository,
26
- },
28
+ imports: [
29
+ TypeOrmModule.forRoot({
30
+ // ...database config (provide your test DB config here)
31
+ entities: [<%= [it.entityName, ...it.allRelatedEntities.filter(e => e !== it.entityName)].join(', ') %>],
32
+ synchronize: true,
33
+ }),
34
+ TypeOrmModule.forFeature([<%= it.entityName %>]),
27
35
  ],
36
+ providers: [<%= it.svcName %>],
28
37
  }).compile();
29
38
 
30
39
  service = module.get<<%= it.svcName %>>(<%= it.svcName %>);
@@ -1,4 +1,4 @@
1
- export type FieldType = "text" | "boolean" | "array" | "enum" | "integer" | "float" | "date" | "json" | "json-plain";
1
+ export type FieldType = "text" | "boolean" | "array" | "enum" | "integer" | "float" | "decimal" | "numeric" | "date" | "json" | "json-plain";
2
2
  export interface Field {
3
3
  name: string;
4
4
  type: FieldType;
@@ -9,6 +9,8 @@ export interface Field {
9
9
  unique?: boolean;
10
10
  default?: string | null;
11
11
  length?: number;
12
+ precision?: number;
13
+ scale?: number;
12
14
  is_email?: boolean;
13
15
  }
14
16
  export interface ComputedField extends Field {
@@ -4,3 +4,4 @@ export declare const getDefaultValueForField: (field: Field) => string | null;
4
4
  export declare const getFieldForTemplate: (fields: Field[], entityName: string) => ComputedField[];
5
5
  export declare const fieldToEnumType: (fieldName: string, entityName: string) => string;
6
6
  export declare const typeExistsInEntity: (entity: Entity, type: string) => number;
7
+ export declare const validateDecimalField: (field: Field) => void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.typeExistsInEntity = exports.fieldToEnumType = exports.getFieldForTemplate = exports.getDefaultValueForField = exports.getJsTypeFromFieldType = void 0;
3
+ exports.validateDecimalField = exports.typeExistsInEntity = exports.fieldToEnumType = exports.getFieldForTemplate = exports.getDefaultValueForField = exports.getJsTypeFromFieldType = void 0;
4
4
  const casing_1 = require("./casing");
5
5
  const typeToJsType = {
6
6
  array: "string[]",
@@ -36,7 +36,16 @@ const typeToJsType = {
36
36
  bytea: "Buffer",
37
37
  point: "{ x: number, y: number }",
38
38
  int4range: "{ lower: number, upper: number, lowerInclusive: boolean, upperInclusive: boolean }",
39
- enum: "string", // handled elsewhere for custom enums
39
+ enum: "string",
40
+ // PostGIS data types
41
+ geometry: "any",
42
+ geography: "any",
43
+ linestring: "{ coordinates: Array<{ x: number, y: number }> }",
44
+ polygon: "{ coordinates: Array<Array<{ x: number, y: number }>> }",
45
+ multipoint: "{ coordinates: Array<{ x: number, y: number }> }",
46
+ multilinestring: "{ coordinates: Array<Array<{ x: number, y: number }>> }",
47
+ multipolygon: "{ coordinates: Array<Array<Array<{ x: number, y: number }>>> }",
48
+ geometrycollection: "{ geometries: Array<any> }",
40
49
  };
41
50
  const getJsTypeFromFieldType = (type) => { var _a; return (_a = typeToJsType[type]) !== null && _a !== void 0 ? _a : "string"; };
42
51
  exports.getJsTypeFromFieldType = getJsTypeFromFieldType;
@@ -70,13 +79,17 @@ const getDefaultValueForField = (field) => {
70
79
  }
71
80
  };
72
81
  exports.getDefaultValueForField = getDefaultValueForField;
73
- const getFieldForTemplate = (fields, entityName) => fields.map((field) => ({
74
- ...field,
75
- default: (0, exports.getDefaultValueForField)(field),
76
- dataType: field.type === "enum"
77
- ? (0, exports.fieldToEnumType)(field.name, entityName)
78
- : (0, exports.getJsTypeFromFieldType)(field.type),
79
- }));
82
+ const getFieldForTemplate = (fields, entityName) => fields.map((field) => {
83
+ // Validate decimal/numeric fields
84
+ (0, exports.validateDecimalField)(field);
85
+ return {
86
+ ...field,
87
+ default: (0, exports.getDefaultValueForField)(field),
88
+ dataType: field.type === "enum"
89
+ ? (0, exports.fieldToEnumType)(field.name, entityName)
90
+ : (0, exports.getJsTypeFromFieldType)(field.type),
91
+ };
92
+ });
80
93
  exports.getFieldForTemplate = getFieldForTemplate;
81
94
  const fieldToEnumType = (fieldName, entityName) => `${(0, casing_1.pascalCase)(entityName)}${(0, casing_1.pascalCase)(fieldName)}Enum`;
82
95
  exports.fieldToEnumType = fieldToEnumType;
@@ -84,3 +97,23 @@ const typeExistsInEntity = (entity, type) => (entity === null || entity === void
84
97
  ? entity === null || entity === void 0 ? void 0 : entity.fields.findIndex((field) => field.type === type)
85
98
  : -1;
86
99
  exports.typeExistsInEntity = typeExistsInEntity;
100
+ const validateDecimalField = (field) => {
101
+ if (field.type !== "decimal" && field.type !== "numeric") {
102
+ return;
103
+ }
104
+ // Validate precision
105
+ if (field.precision !== undefined && (field.precision < 1 || field.precision > 131072)) {
106
+ throw new Error(`${field.name}: Invalid precision value ${field.precision}. Must be between 1 and 131072.`);
107
+ }
108
+ // Validate scale
109
+ if (field.scale !== undefined) {
110
+ if (field.scale < 0 || field.scale > 16383) {
111
+ throw new Error(`${field.name}: Invalid scale value ${field.scale}. Must be between 0 and 16383.`);
112
+ }
113
+ // Validate that scale doesn't exceed precision
114
+ if (field.precision !== undefined && field.scale > field.precision) {
115
+ throw new Error(`${field.name}: Scale (${field.scale}) cannot be greater than precision (${field.precision}).`);
116
+ }
117
+ }
118
+ };
119
+ exports.validateDecimalField = validateDecimalField;
@@ -1,2 +1,6 @@
1
1
  export declare const createFile: (file: string, content: string) => Promise<void>;
2
2
  export declare const createDirectoryContents: (templatePath: string, newProjectPath: string, apiType: string) => void;
3
+ export declare function withGeneratedMeta<T extends object>(data: T): T & {
4
+ generatedAt: string;
5
+ generatedBy: string;
6
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createDirectoryContents = exports.createFile = void 0;
3
+ exports.withGeneratedMeta = exports.createDirectoryContents = exports.createFile = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path = tslib_1.__importStar(require("path"));
6
6
  const fs = tslib_1.__importStar(require("fs"));
@@ -50,3 +50,11 @@ const writeFile = (fileName, origFilePath, writePath, mode, apiType) => {
50
50
  fs.chmodSync(fileWritePath, mode);
51
51
  }
52
52
  };
53
+ function withGeneratedMeta(data) {
54
+ return {
55
+ ...data,
56
+ generatedAt: new Date().toISOString(),
57
+ generatedBy: process.env.USER || process.env.USERNAME || 'unknown',
58
+ };
59
+ }
60
+ exports.withGeneratedMeta = withGeneratedMeta;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getRelationshipsForImport = exports.streamNestedRelationships = exports.getNestedRelationships = exports.generateNestedRelationships = exports.getRelationshipForTemplate = exports.getRelationshipIdField = exports.parseRelationships = exports.parseRelationship = exports.parseManyToMany = exports.parseManytoOne = exports.parseOneToMany = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const pluralize_1 = tslib_1.__importDefault(require("pluralize"));
6
- const casing_1 = require("../../utils/casing");
6
+ const casing_1 = require("../casing");
7
7
  /**
8
8
  * Parses a OneToMany relationship definition.
9
9
  * These are treated as bidirectional from the .apsorc definition.