[ini]
This commit is contained in:
commit
bfed93cf9e
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
temp/
|
||||
build/
|
||||
.geany
|
||||
60
doc/brock.schema.json
Normal file
60
doc/brock.schema.json
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"database": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
},
|
||||
"additionalProperties": {
|
||||
},
|
||||
"required": [
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"kind"
|
||||
]
|
||||
},
|
||||
"backend": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["http", "https"]
|
||||
},
|
||||
"host": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema",
|
||||
"host",
|
||||
"port"
|
||||
]
|
||||
},
|
||||
"frontend": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"database",
|
||||
"backend",
|
||||
"frontend"
|
||||
]
|
||||
}
|
||||
66
doc/examples/contacts.sindri.json
Normal file
66
doc/examples/contacts.sindri.json
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"domains": [
|
||||
{
|
||||
"name": "address",
|
||||
"description": "collection of addresses",
|
||||
"key_field": {
|
||||
"name": "id"
|
||||
},
|
||||
"data_fields": [
|
||||
{
|
||||
"name": "city",
|
||||
"description": "the name of the city",
|
||||
"nullable": false,
|
||||
"type": "string_medium"
|
||||
},
|
||||
{
|
||||
"name": "zip",
|
||||
"description": "the postal code",
|
||||
"nullable": false,
|
||||
"type": "string_medium"
|
||||
},
|
||||
{
|
||||
"name": "street",
|
||||
"description": "the name of the street and the house number",
|
||||
"nullable": false,
|
||||
"type": "string_medium"
|
||||
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "person",
|
||||
"description": "collection of contacts",
|
||||
"key_field": {
|
||||
"name": "id"
|
||||
},
|
||||
"data_fields": [
|
||||
{
|
||||
"name": "prename",
|
||||
"description": "first name of the person",
|
||||
"nullable": false,
|
||||
"type": "string_medium"
|
||||
},
|
||||
{
|
||||
"name": "surname",
|
||||
"description": "last name of the person",
|
||||
"nullable": false,
|
||||
"type": "string_medium"
|
||||
},
|
||||
{
|
||||
"name": "address_id",
|
||||
"description": "reference to the associated address dataset",
|
||||
"nullable": false,
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "email_address",
|
||||
"description": "optional eMail address",
|
||||
"nullable": true,
|
||||
"type": "string_medium",
|
||||
"default": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
13
doc/examples/sweets.sindri.json
Normal file
13
doc/examples/sweets.sindri.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"domains": [
|
||||
{
|
||||
"name": "sweets",
|
||||
"description": "collection of sweets",
|
||||
"key_field": {"name": "id"},
|
||||
"data_fields": [
|
||||
{"name": "name", "type": "string_medium"},
|
||||
{"name": "calories", "type": "integer"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
129
doc/sindri.schema.json
Normal file
129
doc/sindri.schema.json
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"key_field": {
|
||||
"type": [
|
||||
"null",
|
||||
"object"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"data_fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"boolean",
|
||||
"integer",
|
||||
"float",
|
||||
"string_short",
|
||||
"string_medium",
|
||||
"string_long"
|
||||
]
|
||||
},
|
||||
"nullable": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"default": {
|
||||
"type": [
|
||||
"null",
|
||||
"boolean",
|
||||
"integer",
|
||||
"float",
|
||||
"string"
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"constraints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"unique",
|
||||
"foreign_key"
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"additionalProperties": "string",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind"
|
||||
]
|
||||
},
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"domains"
|
||||
]
|
||||
}
|
||||
1880
lib/plankton/plankton.d.ts
vendored
Normal file
1880
lib/plankton/plankton.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
6668
lib/plankton/plankton.js
Normal file
6668
lib/plankton/plankton.js
Normal file
File diff suppressed because it is too large
Load diff
94
readme.md
Normal file
94
readme.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# sindri
|
||||
|
||||
erstellt Datenmodell-Skripte in verschiedenen Ausgabe-Sprachen (MySQL, SQLite, …) auf Basis einer abstrakten Beschreibung
|
||||
|
||||
|
||||
## Erstellung
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
- Typescript-Compiler
|
||||
- GNU Make
|
||||
|
||||
|
||||
### Anweisungen
|
||||
|
||||
- `tools/build` ausführen
|
||||
|
||||
|
||||
## Dokumentation
|
||||
|
||||
Beispiel-Nutzung:
|
||||
|
||||
```sh
|
||||
tools/build
|
||||
cd build
|
||||
|
||||
cat ../doc/examples/contacts.sindri.json | ./sindri -f database:sqlite
|
||||
```
|
||||
|
||||
… erzeugt:
|
||||
|
||||
```sql
|
||||
CREATE TABLE
|
||||
`address`(
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`city` VARCHAR(255) NOT NULL,
|
||||
`zip` VARCHAR(255) NOT NULL,
|
||||
`street` VARCHAR(255) NOT NULL
|
||||
)
|
||||
;
|
||||
|
||||
CREATE TABLE
|
||||
`person`(
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`prename` VARCHAR(255) NOT NULL,
|
||||
`surname` VARCHAR(255) NOT NULL,
|
||||
`address_id` INTEGER NOT NULL,
|
||||
`email_address` VARCHAR(255) DEFAULT NULL
|
||||
)
|
||||
;
|
||||
```
|
||||
|
||||
Der Befehl muss nur minimal gewändert werden um die PostgreSQL-Ausgabe zu erhalten:
|
||||
|
||||
```sh
|
||||
cat ../doc/examples/contacts.sindri.json | ./sindri -f database:postgresql
|
||||
```
|
||||
|
||||
… erzeugt:
|
||||
|
||||
```sql
|
||||
CREATE TABLE
|
||||
address(
|
||||
"id" SERIAL,
|
||||
"city" VARCHAR(255) NOT NULL,
|
||||
"zip" VARCHAR(255) NOT NULL,
|
||||
"street" VARCHAR(255) NOT NULL,
|
||||
UNIQUE ("id")
|
||||
)
|
||||
;
|
||||
COMMENT ON TABLE address IS 'collection of addresses';
|
||||
COMMENT ON COLUMN address.city IS 'the name of the city';
|
||||
COMMENT ON COLUMN address.zip IS 'the postal code';
|
||||
COMMENT ON COLUMN address.street IS 'the name of the street and the house number';
|
||||
|
||||
CREATE TABLE
|
||||
person(
|
||||
"id" SERIAL,
|
||||
"prename" VARCHAR(255) NOT NULL,
|
||||
"surname" VARCHAR(255) NOT NULL,
|
||||
"address_id" INTEGER NOT NULL,
|
||||
"email_address" VARCHAR(255) DEFAULT NULL,
|
||||
UNIQUE ("id")
|
||||
)
|
||||
;
|
||||
COMMENT ON TABLE person IS 'collection of contacts';
|
||||
COMMENT ON COLUMN person.prename IS 'first name of the person';
|
||||
COMMENT ON COLUMN person.surname IS 'last name of the person';
|
||||
COMMENT ON COLUMN person.address_id IS 'reference to the associated address dataset';
|
||||
COMMENT ON COLUMN person.email_address IS 'optional eMail address';
|
||||
```
|
||||
|
||||
|
||||
|
||||
127
source/base.ts
Normal file
127
source/base.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
declare var __dirname;
|
||||
|
||||
namespace _sindri
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_input = {
|
||||
domains : Array<
|
||||
{
|
||||
name : string;
|
||||
description : (null | string);
|
||||
key_field : (
|
||||
null
|
||||
|
|
||||
{
|
||||
name : string;
|
||||
description ?: (null | string);
|
||||
}
|
||||
);
|
||||
data_fields : Array<
|
||||
{
|
||||
name : string;
|
||||
description : (null | string);
|
||||
type : ("boolean" | "integer" | "float" | "string_short" | "string_medium" | "string_long");
|
||||
nullable : boolean;
|
||||
default : (null | boolean | int | float | string);
|
||||
}
|
||||
>;
|
||||
constraints ?: Array<
|
||||
{
|
||||
kind : ("unique" | "foreign_key");
|
||||
parameters : Record<string, any>;
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_output = {
|
||||
render : ((input_data : type_input) => Promise<string>);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export enum enum_realm {
|
||||
database = "database",
|
||||
backend = "backend",
|
||||
frontend = "frontend",
|
||||
other = "other",
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
var _outputs : Record<enum_realm, Record<string, _sindri.type_output>> = {
|
||||
[enum_realm.database]: {},
|
||||
[enum_realm.backend]: {},
|
||||
[enum_realm.frontend]: {},
|
||||
[enum_realm.other]: {},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function add_output(
|
||||
realm : enum_realm,
|
||||
implementation : string,
|
||||
output : _sindri.type_output
|
||||
) : void
|
||||
{
|
||||
_outputs[realm][implementation] = output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function get_output(
|
||||
realm : enum_realm,
|
||||
implementation : string
|
||||
) : _sindri.type_output
|
||||
{
|
||||
return _outputs[realm][implementation];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function list_outputs(
|
||||
) : Array<{realm : enum_realm; implementation : string;}>
|
||||
{
|
||||
return (
|
||||
Object.entries(_outputs)
|
||||
.map(
|
||||
([realm, group]) => (
|
||||
Object.keys(group)
|
||||
.map(
|
||||
implementation => ({"realm": (realm as enum_realm), "implementation": implementation})
|
||||
)
|
||||
)
|
||||
)
|
||||
.reduce(
|
||||
(x, y) => x.concat(y),
|
||||
[]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function get_template(
|
||||
realm : enum_realm,
|
||||
implementation : string,
|
||||
name : string
|
||||
) : Promise<string>
|
||||
{
|
||||
return lib_plankton.file.read(
|
||||
[__dirname, "templates", realm, implementation, name].join("/")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
189
source/conf.ts
Normal file
189
source/conf.ts
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
namespace _sindri
|
||||
{
|
||||
|
||||
/**
|
||||
* @todo generate generic
|
||||
*/
|
||||
export function input_schema(
|
||||
) : any
|
||||
{
|
||||
return {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": ["null", "string"],
|
||||
"default": null
|
||||
},
|
||||
"key_field": {
|
||||
"type": ["null","object"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": ["null", "string"],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"data_fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": ["null", "string"],
|
||||
"default": null
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"boolean",
|
||||
"integer",
|
||||
"float",
|
||||
"string_short",
|
||||
"string_medium",
|
||||
"string_long"
|
||||
]
|
||||
},
|
||||
"nullable": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"default": {
|
||||
"type": ["null", "boolean", "integer", "float", "string"],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"constraints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"unique",
|
||||
"foreign_key"
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"additionalProperties": "string",
|
||||
"properties": {
|
||||
},
|
||||
"required": [
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind"
|
||||
]
|
||||
},
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"domains"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function input_normalize(
|
||||
input_raw : any
|
||||
) : type_input
|
||||
{
|
||||
// validate
|
||||
if (! input_raw.hasOwnProperty("domains")) {
|
||||
throw (new Error("input node is missing mandatory field 'domains'"));
|
||||
}
|
||||
else {
|
||||
// sanitize
|
||||
return {
|
||||
"domains": (
|
||||
input_raw["domains"]
|
||||
.map(
|
||||
domain_raw => ({
|
||||
"name": domain_raw["name"],
|
||||
"description": (domain_raw["description"] ?? null),
|
||||
"key_field": (
|
||||
(domain_raw.hasOwnProperty("key_field") && (domain_raw["key_field"] !== null))
|
||||
? {
|
||||
"name": domain_raw["key_field"]["name"],
|
||||
"description": (domain_raw["key_field"]["description"] ?? null),
|
||||
}
|
||||
: null
|
||||
),
|
||||
"data_fields": (
|
||||
(domain_raw.hasOwnProperty("data_fields") && (domain_raw["data_fields"] !== null))
|
||||
? (
|
||||
domain_raw["data_fields"]
|
||||
.map(
|
||||
data_field_raw => ({
|
||||
"name": data_field_raw["name"],
|
||||
"description": (data_field_raw["description"] ?? null),
|
||||
"type": data_field_raw["type"],
|
||||
"nullable": (data_field_raw["nullable"] ?? true),
|
||||
"default": data_field_raw["default"],
|
||||
})
|
||||
)
|
||||
)
|
||||
: []
|
||||
),
|
||||
"constraints": (
|
||||
(domain_raw.hasOwnProperty("constraints") && (domain_raw["constraints"] !== null))
|
||||
? (
|
||||
domain_raw["constraints"]
|
||||
.map(
|
||||
constraint_raw => ({
|
||||
"kind": constraint_raw["kind"],
|
||||
"parameters": (constraint_raw["parameters"] ?? {}),
|
||||
})
|
||||
)
|
||||
)
|
||||
: []
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
135
source/main.ts
Normal file
135
source/main.ts
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
namespace _sindri
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function main(
|
||||
args_raw : Array<string>
|
||||
) : Promise<void>
|
||||
{
|
||||
const arg_handler = new lib_plankton.args.class_handler(
|
||||
{
|
||||
"format": new lib_plankton.args.class_argument({
|
||||
"name": "format",
|
||||
"type": lib_plankton.args.enum_type.string,
|
||||
"kind": lib_plankton.args.enum_kind.volatile,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": "database:sqlite",
|
||||
"parameters": {
|
||||
"indicators_long": ["format"],
|
||||
"indicators_short": ["f"],
|
||||
},
|
||||
"info": "output format",
|
||||
}),
|
||||
"list": new lib_plankton.args.class_argument({
|
||||
"name": "list",
|
||||
"type": lib_plankton.args.enum_type.boolean,
|
||||
"kind": lib_plankton.args.enum_kind.volatile,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": false,
|
||||
"parameters": {
|
||||
"indicators_long": ["list"],
|
||||
"indicators_short": ["l"],
|
||||
},
|
||||
"info": "list available output formats",
|
||||
}),
|
||||
"schema": new lib_plankton.args.class_argument({
|
||||
"name": "schema",
|
||||
"type": lib_plankton.args.enum_type.boolean,
|
||||
"kind": lib_plankton.args.enum_kind.volatile,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": false,
|
||||
"parameters": {
|
||||
"indicators_long": ["schema"],
|
||||
"indicators_short": ["s"],
|
||||
},
|
||||
"info": "print sindri JSON schema to stdout and exit",
|
||||
}),
|
||||
"help": new lib_plankton.args.class_argument({
|
||||
"name": "help",
|
||||
"type": lib_plankton.args.enum_type.boolean,
|
||||
"kind": lib_plankton.args.enum_kind.volatile,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": false,
|
||||
"parameters": {
|
||||
"indicators_long": ["help"],
|
||||
"indicators_short": ["h"],
|
||||
},
|
||||
"info": "print help to stdout and exit",
|
||||
}),
|
||||
}
|
||||
);
|
||||
const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" "));
|
||||
|
||||
if (args["help"]) {
|
||||
process.stdout.write(
|
||||
arg_handler.generate_help(
|
||||
{
|
||||
"programname": "sindri",
|
||||
"author": "Christian Fraß <frass@greenscale.de>",
|
||||
"description": "create data model scripts in different output formats (MySQL, SQLite, …) on basis of an abstract description; feed with .sindri.json file via stdin!",
|
||||
"executable": "sindri",
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (args["schema"]) {
|
||||
process.stdout.write(
|
||||
JSON.stringify(_sindri.input_schema(), undefined, "\t")
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (args["list"]) {
|
||||
process.stdout.write(
|
||||
_sindri.list_outputs()
|
||||
.map(
|
||||
entry => lib_plankton.string.coin(
|
||||
"{{realm}}:{{implementation}}\n",
|
||||
{
|
||||
"realm": entry.realm,
|
||||
"implementation": entry.implementation,
|
||||
}
|
||||
)
|
||||
)
|
||||
.join("")
|
||||
);
|
||||
}
|
||||
else {
|
||||
const input_content : string = await lib_plankton.file.read_stdin();
|
||||
const input_data_raw : any = lib_plankton.json.decode(input_content);
|
||||
const input_data : type_input = _sindri.input_normalize(input_data_raw);
|
||||
|
||||
const format_parts : Array<string> = args["format"].split(":");
|
||||
const realm_encoded : string = format_parts[0];
|
||||
const realm : _sindri.enum_realm = {
|
||||
"database": _sindri.enum_realm.database,
|
||||
"backend": _sindri.enum_realm.backend,
|
||||
"frontend": _sindri.enum_realm.frontend,
|
||||
"other": _sindri.enum_realm.other,
|
||||
}[realm_encoded];
|
||||
const name : string = format_parts.slice(1).join(":");
|
||||
|
||||
let output : (null | _sindri.type_output);
|
||||
try {
|
||||
output = _sindri.get_output(realm, name);
|
||||
}
|
||||
catch (error) {
|
||||
output = null;
|
||||
}
|
||||
if (output === null) {
|
||||
throw (new Error("unhandled output format: " + args["format"]));
|
||||
}
|
||||
else {
|
||||
const output_content : string = await output.render(input_data);
|
||||
process.stdout.write(output_content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
_sindri.main(process.argv.slice(2));
|
||||
339
source/outputs/backend/typescript/logic.ts
Normal file
339
source/outputs/backend/typescript/logic.ts
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
namespace _sindri.outputs.backend.typescript
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
async function get_template(
|
||||
name : string
|
||||
) : Promise<string>
|
||||
{
|
||||
return _sindri.get_template(_sindri.enum_realm.backend, "typescript", name + ".ts.tpl");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function coin(
|
||||
template_name : string,
|
||||
values : Record<string, string>
|
||||
) : Promise<string>
|
||||
{
|
||||
return lib_plankton.string.coin(
|
||||
await get_template(template_name),
|
||||
values,
|
||||
{
|
||||
"open": "<<",
|
||||
"close": ">>",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function render(
|
||||
input_data
|
||||
) : Promise<string>
|
||||
{
|
||||
// TODO as command line argument?
|
||||
const conf_internal : {
|
||||
namespace_base : string;
|
||||
} = {
|
||||
"namespace_base": "_sindri.",
|
||||
};
|
||||
|
||||
const map_primitive_type = function (typename : string) : lib_plankton.prog.struct_type {
|
||||
const mymap : Record<string, lib_plankton.prog.struct_type> = {
|
||||
"boolean": new lib_plankton.prog.struct_type_boolean(),
|
||||
"integer": new lib_plankton.prog.struct_type_integer(),
|
||||
// "float": new lib_plankton.prog.struct_type_integer(),
|
||||
"string_short": new lib_plankton.prog.struct_type_string(),
|
||||
"string_medium": new lib_plankton.prog.struct_type_string(),
|
||||
"string_long": new lib_plankton.prog.struct_type_string(),
|
||||
};
|
||||
return mymap[typename];
|
||||
};
|
||||
const name_table = function (domain) : string {return (domain.name);};
|
||||
const database_path = lib_plankton.string.coin(
|
||||
"/tmp/{{name}}.sqlite",
|
||||
{
|
||||
"name": "sindri",
|
||||
}
|
||||
);
|
||||
const namespace_entity = function (domain) {
|
||||
return lib_plankton.string.coin(
|
||||
"{{base}}entities.{{domain_name}}",
|
||||
{
|
||||
"base": conf_internal.namespace_base,
|
||||
"domain_name": domain.name,
|
||||
}
|
||||
);
|
||||
};
|
||||
const name_entity_type = function (fully_qualified, domain) : string {
|
||||
return lib_plankton.string.coin(
|
||||
"{{prefix}}type_value",
|
||||
{
|
||||
"prefix": (
|
||||
fully_qualified
|
||||
? (namespace_entity(domain) + ".")
|
||||
: ""
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
const namespace_repository = function (domain) {
|
||||
return lib_plankton.string.coin(
|
||||
"{{base}}repositories",
|
||||
{
|
||||
"base": conf_internal.namespace_base,
|
||||
}
|
||||
);
|
||||
};
|
||||
const name_repository_function = function (fully_qualified, domain, action) : string {
|
||||
return lib_plankton.string.coin(
|
||||
"{{prefix}}{{action}}",
|
||||
{
|
||||
"prefix": (
|
||||
fully_qualified
|
||||
? namespace_repository(domain)
|
||||
: ""
|
||||
),
|
||||
"action": action,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return coin(
|
||||
"master",
|
||||
{
|
||||
"namespace_base": conf_internal.namespace_base,
|
||||
"entities": (
|
||||
(await Promise.all(
|
||||
input_data["domains"]
|
||||
.map(
|
||||
(domain) => coin(
|
||||
"entity",
|
||||
{
|
||||
"domain_name": domain.name,
|
||||
"defs": lib_plankton.prog.typescript.render_statement(
|
||||
new lib_plankton.prog.struct_statement_type_definition(
|
||||
name_entity_type(false, domain),
|
||||
new lib_plankton.prog.struct_type_record(
|
||||
domain.data_fields
|
||||
.map(
|
||||
(data_field) => ({
|
||||
"name": data_field.name,
|
||||
"type": (
|
||||
data_field.nullable
|
||||
? new lib_plankton.prog.struct_type_union(
|
||||
new lib_plankton.prog.struct_type_literal(
|
||||
new lib_plankton.prog.struct_expression_literal(
|
||||
null
|
||||
)
|
||||
),
|
||||
map_primitive_type(data_field["type"])
|
||||
)
|
||||
: map_primitive_type(data_field["type"])
|
||||
),
|
||||
"mandatory": true,
|
||||
})
|
||||
)
|
||||
),
|
||||
{
|
||||
"export": true,
|
||||
}
|
||||
),
|
||||
{
|
||||
"level": 2,
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
))
|
||||
.join("")
|
||||
),
|
||||
"repositories": (
|
||||
(await Promise.all(
|
||||
input_data["domains"]
|
||||
.map(
|
||||
(domain) => coin(
|
||||
"repository",
|
||||
{
|
||||
"domain_name": domain.name,
|
||||
"type_name": name_entity_type(true, domain),
|
||||
"table_name": name_table(domain),
|
||||
"list_function_name": name_repository_function(false, domain, "list"),
|
||||
"list_query_fields": (
|
||||
[domain.key_field.name]
|
||||
.concat(domain.data_fields.map(field => field.name))
|
||||
.join(",")
|
||||
),
|
||||
"list_result": lib_plankton.prog.typescript.render_expression(
|
||||
new lib_plankton.prog.struct_expression_dict(
|
||||
[
|
||||
{
|
||||
"key": "key",
|
||||
"value": new lib_plankton.prog.struct_expression_projection(
|
||||
new lib_plankton.prog.struct_expression_variable("row"),
|
||||
new lib_plankton.prog.struct_expression_literal(domain.key_field.name)
|
||||
),
|
||||
},
|
||||
{
|
||||
"key": "value",
|
||||
"value": new lib_plankton.prog.struct_expression_dict(
|
||||
domain.data_fields
|
||||
.map(
|
||||
(field, index) => ({
|
||||
"key": field.name,
|
||||
"value": new lib_plankton.prog.struct_expression_projection(
|
||||
new lib_plankton.prog.struct_expression_variable("row"),
|
||||
new lib_plankton.prog.struct_expression_literal(field.name)
|
||||
)
|
||||
})
|
||||
)
|
||||
),
|
||||
},
|
||||
]
|
||||
),
|
||||
{
|
||||
"indent": false,
|
||||
"level": 8,
|
||||
}
|
||||
),
|
||||
"read_function_name": name_repository_function(false, domain, "read"),
|
||||
"read_query_fields": (
|
||||
[]
|
||||
.concat(domain.data_fields.map(field => field.name))
|
||||
.join(",")
|
||||
),
|
||||
"read_result_fields": lib_plankton.prog.typescript.render_expression(
|
||||
new lib_plankton.prog.struct_expression_dict(
|
||||
domain.data_fields
|
||||
.map(
|
||||
(field, index) => ({
|
||||
"key": field.name,
|
||||
"value": new lib_plankton.prog.struct_expression_projection(
|
||||
new lib_plankton.prog.struct_expression_variable("row"),
|
||||
new lib_plankton.prog.struct_expression_literal(field.name)
|
||||
)
|
||||
})
|
||||
)
|
||||
),
|
||||
{
|
||||
"indent": false,
|
||||
"level": 6,
|
||||
}
|
||||
),
|
||||
"create_function_name": name_repository_function(false, domain, "create"),
|
||||
"create_query_field_names": (
|
||||
domain.data_fields
|
||||
.map(field => field.name)
|
||||
.join(",")
|
||||
),
|
||||
"create_query_field_placeholders": (
|
||||
domain.data_fields
|
||||
.map(field => (":" + field.name))
|
||||
.join(",")
|
||||
),
|
||||
"create_query_field_values": lib_plankton.prog.typescript.render_expression(
|
||||
new lib_plankton.prog.struct_expression_dict(
|
||||
domain.data_fields
|
||||
.map(
|
||||
field => ({
|
||||
"key": field.name,
|
||||
"value": new lib_plankton.prog.struct_expression_fieldaccess(
|
||||
new lib_plankton.prog.struct_expression_variable("value"),
|
||||
field.name,
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
{
|
||||
"indent": false,
|
||||
"level": 6,
|
||||
}
|
||||
),
|
||||
"update_function_name": name_repository_function(false, domain, "update"),
|
||||
"update_query_assignments": (
|
||||
domain.data_fields
|
||||
.map(
|
||||
field => lib_plankton.string.coin(
|
||||
"{{key}} = {{value}}",
|
||||
{
|
||||
"key": field.name,
|
||||
"value": (":" + ("value_" + field.name)),
|
||||
}
|
||||
)
|
||||
)
|
||||
.join(", ")
|
||||
),
|
||||
"update_query_values": lib_plankton.prog.typescript.render_expression(
|
||||
new lib_plankton.prog.struct_expression_dict(
|
||||
[]
|
||||
.concat(
|
||||
[
|
||||
{
|
||||
"key": "key",
|
||||
"value": new lib_plankton.prog.struct_expression_variable("key"),
|
||||
},
|
||||
]
|
||||
)
|
||||
.concat(
|
||||
domain.data_fields
|
||||
.map(
|
||||
field => ({
|
||||
"key": ("value_" + field.name),
|
||||
"value": new lib_plankton.prog.struct_expression_fieldaccess(
|
||||
new lib_plankton.prog.struct_expression_variable("value"),
|
||||
field.name,
|
||||
),
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
{
|
||||
"indent": false,
|
||||
"level": 7,
|
||||
}
|
||||
),
|
||||
"delete_function_name": name_repository_function(false, domain, "delete"),
|
||||
}
|
||||
)
|
||||
)
|
||||
))
|
||||
.join("")
|
||||
),
|
||||
"api": (
|
||||
(await Promise.all(
|
||||
input_data["domains"]
|
||||
.map(
|
||||
(domain) => coin(
|
||||
"api",
|
||||
{
|
||||
"domain_name": domain.name,
|
||||
"type_name": name_entity_type(true, domain),
|
||||
"repository_function_list": name_repository_function(true, domain, "list"),
|
||||
"repository_function_read": name_repository_function(true, domain, "read"),
|
||||
"repository_function_create": name_repository_function(true, domain, "create"),
|
||||
"repository_function_update": name_repository_function(true, domain, "update"),
|
||||
"repository_function_delete": name_repository_function(true, domain, "delete"),
|
||||
}
|
||||
)
|
||||
)
|
||||
))
|
||||
.join("")
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_sindri.add_output(
|
||||
_sindri.enum_realm.backend,
|
||||
"typescript",
|
||||
{
|
||||
"render": _sindri.outputs.backend.typescript.render,
|
||||
}
|
||||
);
|
||||
118
source/outputs/backend/typescript/templates/api.ts.tpl
Normal file
118
source/outputs/backend/typescript/templates/api.ts.tpl
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// <<domain_name>>
|
||||
{
|
||||
lib_plankton.rest.register(
|
||||
rest,
|
||||
lib_plankton.http.enum_method.get,
|
||||
lib_plankton.string.coin(
|
||||
"/{{base_path}}{{domain_name}}",
|
||||
{
|
||||
"base_path": _brock.conf.api_base_path,
|
||||
"domain_name": "<<domain_name>>",
|
||||
}
|
||||
),
|
||||
{
|
||||
"execution": async function (stuff) {
|
||||
return {
|
||||
"status_code": 200,
|
||||
"data": await <<repository_function_list>>(
|
||||
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
lib_plankton.rest.register(
|
||||
rest,
|
||||
lib_plankton.http.enum_method.get,
|
||||
lib_plankton.string.coin(
|
||||
"/{{base_path}}{{domain_name}}/:id",
|
||||
{
|
||||
"base_path": _brock.conf.api_base_path,
|
||||
"domain_name": "<<domain_name>>",
|
||||
}
|
||||
),
|
||||
{
|
||||
"execution": async function (stuff) {
|
||||
return {
|
||||
"status_code": 200,
|
||||
"data": await <<repository_function_read>>(
|
||||
parseInt(
|
||||
stuff.path_parameters["id"]
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
lib_plankton.rest.register(
|
||||
rest,
|
||||
lib_plankton.http.enum_method.post,
|
||||
lib_plankton.string.coin(
|
||||
"/{{base_path}}{{domain_name}}",
|
||||
{
|
||||
"base_path": _brock.conf.api_base_path,
|
||||
"domain_name": "<<domain_name>>",
|
||||
}
|
||||
),
|
||||
{
|
||||
"execution": async function (stuff) {
|
||||
const id = await <<repository_function_create>>(
|
||||
(stuff.input as <<type_name>>)
|
||||
);
|
||||
return {
|
||||
"status_code": 201,
|
||||
"data": id
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
lib_plankton.rest.register(
|
||||
rest,
|
||||
lib_plankton.http.enum_method.patch,
|
||||
lib_plankton.string.coin(
|
||||
"/{{base_path}}{{domain_name}}/:id",
|
||||
{
|
||||
"base_path": _brock.conf.api_base_path,
|
||||
"domain_name": "<<domain_name>>",
|
||||
}
|
||||
),
|
||||
{
|
||||
"execution": async function (stuff) {
|
||||
const dummy = await <<repository_function_create>>(
|
||||
parseInt(
|
||||
stuff.path_parameters["id"]
|
||||
),
|
||||
(stuff.input as <<type_name>>)
|
||||
);
|
||||
return {
|
||||
"status_code": 200,
|
||||
"data": null
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
lib_plankton.rest.register(
|
||||
rest,
|
||||
lib_plankton.http.enum_method.delete,
|
||||
lib_plankton.string.coin(
|
||||
"/{{base_path}}{{domain_name}}/:id",
|
||||
{
|
||||
"base_path": _brock.conf.api_base_path,
|
||||
"domain_name": "<<domain_name>>",
|
||||
}
|
||||
),
|
||||
{
|
||||
"execution": async function (stuff) {
|
||||
const dummy = await <<repository_function_delete>>(
|
||||
parseInt(
|
||||
stuff.path_parameters["id"]
|
||||
)
|
||||
);
|
||||
return {
|
||||
"status_code": 200,
|
||||
"data": null
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export namespace <<domain_name>>
|
||||
{
|
||||
<<defs>>
|
||||
}
|
||||
74
source/outputs/backend/typescript/templates/master.ts.tpl
Normal file
74
source/outputs/backend/typescript/templates/master.ts.tpl
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// declare var require;
|
||||
|
||||
namespace <<namespace_base>>entities
|
||||
{
|
||||
<<entities>>
|
||||
}
|
||||
|
||||
|
||||
namespace <<namespace_base>>repositories
|
||||
{
|
||||
<<repositories>>
|
||||
}
|
||||
|
||||
|
||||
namespace <<namespace_base>>main
|
||||
{
|
||||
// run
|
||||
export function run(
|
||||
) : void
|
||||
{
|
||||
lib_plankton.log.conf_push(
|
||||
[
|
||||
new lib_plankton.log.class_channel_stdout(
|
||||
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
// define api
|
||||
{
|
||||
// meta
|
||||
{
|
||||
lib_plankton.rest.register(
|
||||
rest,
|
||||
lib_plankton.http.enum_method.get,
|
||||
"/_spec",
|
||||
{
|
||||
"execution": async function (stuff) {
|
||||
return {
|
||||
"status_code": 200,
|
||||
"data": lib_plankton.rest.to_oas(
|
||||
rest
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
<<api>>
|
||||
}
|
||||
|
||||
// setup server
|
||||
const server = lib_plankton.server.make(
|
||||
_brock.conf.server_port,
|
||||
async function (input) {
|
||||
const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(
|
||||
input
|
||||
);
|
||||
const http_response : lib_plankton.http.type_response = await lib_plankton.rest.call(
|
||||
rest,
|
||||
http_request
|
||||
);
|
||||
return lib_plankton.http.encode_response(
|
||||
http_response
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// start
|
||||
lib_plankton.server.start(
|
||||
server
|
||||
);
|
||||
}
|
||||
}
|
||||
113
source/outputs/backend/typescript/templates/repository.ts.tpl
Normal file
113
source/outputs/backend/typescript/templates/repository.ts.tpl
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
export namespace <<domain_name>>
|
||||
{
|
||||
|
||||
// list
|
||||
export function <<list_function_name>>(
|
||||
) : Promise<Array<{key : number; value : <<type_name>>;}>>
|
||||
{
|
||||
return (
|
||||
lib_plankton.sqlite.query_get(
|
||||
_brock.conf.database_path,
|
||||
{
|
||||
"template": "SELECT <<list_query_fields>> FROM <<table_name>>;",
|
||||
"arguments": {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(
|
||||
(rows) => rows.map(
|
||||
(row) => <<list_result>>
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// read
|
||||
export function <<read_function_name>>(
|
||||
key : number
|
||||
) : Promise<<<type_name>>>
|
||||
{
|
||||
return (
|
||||
lib_plankton.sqlite.query_get(
|
||||
_brock.conf.database_path,
|
||||
{
|
||||
"template": "SELECT <<read_query_fields>> FROM <<table_name>> WHERE (id = :key);",
|
||||
"arguments": {
|
||||
"key": key
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(
|
||||
(rows) {
|
||||
const row = rows[0];
|
||||
return <<read_result_fields>>;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// create
|
||||
export function <<create_function_name>>(
|
||||
value : <<type_name>>
|
||||
) : Promise<number>
|
||||
{
|
||||
return (
|
||||
lib_plankton.sqlite.query_put(
|
||||
_brock.conf.database_path,
|
||||
{
|
||||
"template": "INSERT INTO <<table_name>>(<<create_query_field_names>>) VALUES (<<create_query_field_placeholders>>);",
|
||||
"arguments": <<create_query_field_values>>
|
||||
}
|
||||
)
|
||||
.then(
|
||||
(result) => result.id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// update
|
||||
export function <<update_function_name>>(
|
||||
key : number,
|
||||
value : <<type_name>>
|
||||
) : Promise<void>
|
||||
{
|
||||
return (
|
||||
lib_plankton.sqlite.query_put(
|
||||
_brock.conf.database_path,
|
||||
{
|
||||
"template": "UPDATE <<table_name>> SET <<update_query_assignments>> WHERE (id = :key);",
|
||||
"arguments": {
|
||||
"key": key,
|
||||
<<update_query_values>>
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(
|
||||
(result) => {}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// delete
|
||||
export function <<delete_function_name>>(
|
||||
key : number
|
||||
) : Promise<void>
|
||||
{
|
||||
return (
|
||||
lib_plankton.sqlite.query_put(
|
||||
_brock.conf.database_path,
|
||||
{
|
||||
"template": "DELETE FROM <<table_name>> WHERE (id = :key);",
|
||||
"arguments": {
|
||||
"key": key
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(
|
||||
(result) => {}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
221
source/outputs/database/mysql/logic.ts
Normal file
221
source/outputs/database/mysql/logic.ts
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
namespace _sindri.outputs.database.mysql
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
function value_encode(
|
||||
value : any
|
||||
) : string
|
||||
{
|
||||
if (value === null) {
|
||||
return "NULL";
|
||||
}
|
||||
else {
|
||||
switch (typeof(value)) {
|
||||
case "boolean": {
|
||||
return (value ? "TRUE" : "FALSE");
|
||||
break;
|
||||
}
|
||||
case "number": {
|
||||
return value.toString();
|
||||
break;
|
||||
}
|
||||
case "string": {
|
||||
return ("'" + value + "'");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw (new Error("unhandled"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function render(
|
||||
input_data
|
||||
) : string
|
||||
{
|
||||
return (
|
||||
input_data.domains
|
||||
.map(
|
||||
(domain) => lib_plankton.string.coin(
|
||||
"CREATE TABLE\n\t`{{name}}`(\n{{entries}}\n\t){{comment}}\n;",
|
||||
{
|
||||
"name": domain.name,
|
||||
"comment": (
|
||||
(domain.description === null)
|
||||
? ""
|
||||
: lib_plankton.string.coin(
|
||||
" COMMENT '{{comment}}'",
|
||||
{
|
||||
"comment": domain.description,
|
||||
}
|
||||
)
|
||||
),
|
||||
"entries": (
|
||||
(
|
||||
[]
|
||||
// key field
|
||||
.concat(
|
||||
(domain.key_field === null)
|
||||
? []
|
||||
: lib_plankton.string.coin(
|
||||
"`{{name}}` {{parameters}}",
|
||||
{
|
||||
"name": domain.key_field.name,
|
||||
"parameters": (
|
||||
[
|
||||
"INTEGER",
|
||||
"PRIMARY KEY",
|
||||
"AUTO INCREMENT",
|
||||
]
|
||||
.concat(
|
||||
(domain.key_field.description === null)
|
||||
? []
|
||||
: [
|
||||
lib_plankton.string.coin(
|
||||
"COMMENT '{{comment}}'",
|
||||
{
|
||||
"comment": domain.key_field.description,
|
||||
}
|
||||
),
|
||||
]
|
||||
)
|
||||
.join(" ")
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
// data fields
|
||||
.concat(
|
||||
domain.data_fields
|
||||
.map(
|
||||
(data_field) => lib_plankton.string.coin(
|
||||
"`{{name}}` {{parameters}}",
|
||||
{
|
||||
"name": data_field.name,
|
||||
"parameters": (
|
||||
(
|
||||
// type
|
||||
[
|
||||
{
|
||||
"boolean": "BOOLEAN",
|
||||
"integer": "INTEGER",
|
||||
"string_short": "VARCHAR(63)",
|
||||
"string_medium": "VARCHAR(255)",
|
||||
"string_long": "TEXT",
|
||||
}[data_field.type],
|
||||
]
|
||||
// nullability
|
||||
.concat(
|
||||
data_field.nullable
|
||||
? ["NULL"]
|
||||
: []
|
||||
)
|
||||
// default
|
||||
.concat(
|
||||
(data_field.default === undefined)
|
||||
? []
|
||||
: [
|
||||
lib_plankton.string.coin(
|
||||
"DEFAULT {{value}}",
|
||||
{
|
||||
"value": value_encode(data_field.default),
|
||||
}
|
||||
),
|
||||
]
|
||||
)
|
||||
// comment
|
||||
.concat(
|
||||
(data_field.description === null)
|
||||
? []
|
||||
: [
|
||||
lib_plankton.string.coin(
|
||||
"COMMENT '{{comment}}'",
|
||||
{
|
||||
"comment": data_field.description,
|
||||
}
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
.join(" ")
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
// constraints
|
||||
.concat(
|
||||
domain["constraints"]
|
||||
.map(
|
||||
(constraint) => {
|
||||
switch (constraint.kind) {
|
||||
default: {
|
||||
throw (new Error("unhandled constraint kind: " + constraint.kind));
|
||||
break;
|
||||
}
|
||||
case "foreign_key": {
|
||||
return lib_plankton.string.coin(
|
||||
"FOREIGN KEY ({{fields}}) REFERENCES `{{reference_name}}`({{reference_fields}})",
|
||||
{
|
||||
"fields": (
|
||||
constraint.parameters["fields"]
|
||||
.map(x => ('`' + x + '`'))
|
||||
.join(",")
|
||||
),
|
||||
"reference_name": constraint.parameters["reference"]["name"],
|
||||
"reference_fields": (
|
||||
constraint.parameters["reference"]["fields"]
|
||||
.map(x => ('`' + x + '`'))
|
||||
.join(",")
|
||||
),
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "unique": {
|
||||
return lib_plankton.string.coin(
|
||||
"UNIQUE ({{fields}})",
|
||||
{
|
||||
"fields": (
|
||||
constraint.parameters["fields"]
|
||||
.map(x => ('`' + x + '`'))
|
||||
.join(",")
|
||||
),
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(x => ("\t\t" + x))
|
||||
.join(",\n")
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
.map(x => (x + "\n"))
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
_sindri.add_output(
|
||||
_sindri.enum_realm.database,
|
||||
"mysql",
|
||||
{
|
||||
"render": (x) => Promise.resolve<string>(_sindri.outputs.database.mysql.render(x)),
|
||||
}
|
||||
);
|
||||
255
source/outputs/database/postgresql/logic.ts
Normal file
255
source/outputs/database/postgresql/logic.ts
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
namespace _sindri.outputs.database.postgresql
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
function value_encode(
|
||||
value : any
|
||||
) : string
|
||||
{
|
||||
if (value === null) {
|
||||
return "NULL";
|
||||
}
|
||||
else {
|
||||
switch (typeof(value)) {
|
||||
case "boolean": {
|
||||
return (value ? "TRUE" : "FALSE");
|
||||
break;
|
||||
}
|
||||
case "number": {
|
||||
return value.toString();
|
||||
break;
|
||||
}
|
||||
case "string": {
|
||||
return ("'" + value + "'");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw (new Error("unhandled"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function render(
|
||||
input_data
|
||||
) : string
|
||||
{
|
||||
return (
|
||||
input_data.domains
|
||||
.map(
|
||||
(domain) => lib_plankton.string.coin(
|
||||
"CREATE TABLE\n\t{{name}}(\n{{entries}}\n\t)\n;\n{{comments}}",
|
||||
{
|
||||
"name": domain.name,
|
||||
"entries": (
|
||||
(
|
||||
[]
|
||||
// key field
|
||||
.concat(
|
||||
(domain.key_field === null)
|
||||
? []
|
||||
: lib_plankton.string.coin(
|
||||
"{{name}} {{parameters}}",
|
||||
{
|
||||
"name": ('"' + domain.key_field.name + '"'),
|
||||
"parameters": (
|
||||
[
|
||||
"SERIAL",
|
||||
]
|
||||
.join(" ")
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
// data fields
|
||||
.concat(
|
||||
domain.data_fields
|
||||
.map(
|
||||
(data_field) => lib_plankton.string.coin(
|
||||
"{{name}} {{parameters}}",
|
||||
{
|
||||
"name": ('"' + data_field.name + '"'),
|
||||
"parameters": (
|
||||
(
|
||||
// type
|
||||
[
|
||||
{
|
||||
"boolean": "BOOLEAN",
|
||||
"integer": "INTEGER",
|
||||
"string_short": "VARCHAR(63)",
|
||||
"string_medium": "VARCHAR(255)",
|
||||
"string_long": "TEXT",
|
||||
}[data_field.type],
|
||||
]
|
||||
// nullability
|
||||
.concat(
|
||||
data_field.nullable
|
||||
? []
|
||||
: ["NOT NULL"]
|
||||
)
|
||||
// default
|
||||
.concat(
|
||||
(data_field.default === undefined)
|
||||
? []
|
||||
: [
|
||||
lib_plankton.string.coin(
|
||||
"DEFAULT {{value}}",
|
||||
{
|
||||
"value": value_encode(data_field.default),
|
||||
}
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
.join(" ")
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
// constraints
|
||||
.concat(
|
||||
domain["constraints"]
|
||||
.concat(
|
||||
(domain.key_field === null)
|
||||
?
|
||||
[]
|
||||
:
|
||||
[
|
||||
{
|
||||
"kind": "unique",
|
||||
"parameters": {
|
||||
"fields": [
|
||||
domain.key_field.name,
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
)
|
||||
.map(
|
||||
(constraint) => {
|
||||
switch (constraint.kind) {
|
||||
default: {
|
||||
throw (new Error("unhandled constraint kind: " + constraint.kind));
|
||||
break;
|
||||
}
|
||||
case "foreign_key": {
|
||||
return lib_plankton.string.coin(
|
||||
"FOREIGN KEY ({{fields}}) REFERENCES {{reference_name}}({{reference_fields}})",
|
||||
{
|
||||
"fields": (
|
||||
constraint.parameters["fields"]
|
||||
.map(x => ('"' + x + '"'))
|
||||
.join(",")
|
||||
),
|
||||
"reference_name": ('"' + constraint.parameters["reference"]["name"] + '"'),
|
||||
"reference_fields": (
|
||||
constraint.parameters["reference"]["fields"]
|
||||
.map(x => ('"' + x + '"'))
|
||||
.join(",")
|
||||
),
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "unique": {
|
||||
return lib_plankton.string.coin(
|
||||
"UNIQUE ({{fields}})",
|
||||
{
|
||||
"fields": (
|
||||
constraint.parameters["fields"]
|
||||
.map(x => ('"' + x + '"'))
|
||||
.join(",")
|
||||
),
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(x => ("\t\t" + x))
|
||||
.join(",\n")
|
||||
),
|
||||
"comments": (
|
||||
(
|
||||
[]
|
||||
.concat(
|
||||
(! (domain.description === null))
|
||||
? [
|
||||
{
|
||||
"kind": "TABLE",
|
||||
"subject": domain.name,
|
||||
"value": domain.description,
|
||||
}
|
||||
]
|
||||
: []
|
||||
)
|
||||
.concat(
|
||||
(
|
||||
(! (domain.key_field === null))
|
||||
&&
|
||||
(! (domain.key_field.description === null))
|
||||
)
|
||||
? [
|
||||
{
|
||||
"kind": "COLUMN",
|
||||
"subject": (domain.name + "." + domain.key_field.name),
|
||||
"value": domain.key_field.description,
|
||||
}
|
||||
]
|
||||
: []
|
||||
)
|
||||
.concat(
|
||||
domain.data_fields
|
||||
.filter(
|
||||
data_field => (! (data_field.description === null))
|
||||
)
|
||||
.map(
|
||||
data_field => ({
|
||||
"kind": "COLUMN",
|
||||
"subject": (domain.name + "." + data_field.name),
|
||||
"value": data_field.description,
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(
|
||||
entry => lib_plankton.string.coin(
|
||||
"COMMENT ON {{kind}} {{subject}} IS '{{value}}';",
|
||||
{
|
||||
"kind": entry.kind,
|
||||
"subject": entry.subject,
|
||||
"value": entry.value,
|
||||
}
|
||||
)
|
||||
)
|
||||
.join("\n")
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
.map(x => (x + "\n"))
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
_sindri.add_output(
|
||||
_sindri.enum_realm.database,
|
||||
"postgresql",
|
||||
{
|
||||
"render": (x) => Promise.resolve<string>(_sindri.outputs.database.postgresql.render(x)),
|
||||
}
|
||||
);
|
||||
183
source/outputs/database/sqlite/logic.ts
Normal file
183
source/outputs/database/sqlite/logic.ts
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
namespace _sindri.outputs.database.sqlite
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
function value_encode(
|
||||
value : any
|
||||
) : string
|
||||
{
|
||||
if (value === null) {
|
||||
return "NULL";
|
||||
}
|
||||
else {
|
||||
switch (typeof(value)) {
|
||||
case "boolean": {
|
||||
return (value ? "TRUE" : "FALSE");
|
||||
break;
|
||||
}
|
||||
case "number": {
|
||||
return value.toString();
|
||||
break;
|
||||
}
|
||||
case "string": {
|
||||
return ("'" + value + "'");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw (new Error("unhandled"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function render(
|
||||
input_data
|
||||
) : string
|
||||
{
|
||||
return (
|
||||
input_data["domains"]
|
||||
.map(
|
||||
(domain) => lib_plankton.string.coin(
|
||||
"CREATE TABLE\n\t`{{name}}`(\n{{entries}}\n\t)\n;",
|
||||
{
|
||||
"name": domain.name,
|
||||
"entries": (
|
||||
(
|
||||
[]
|
||||
// key field
|
||||
.concat(
|
||||
(domain.key_field === null)
|
||||
? []
|
||||
: lib_plankton.string.coin(
|
||||
"`{{name}}` {{parameters}}",
|
||||
{
|
||||
"name": domain.key_field.name,
|
||||
"parameters": (
|
||||
[
|
||||
"INTEGER",
|
||||
"PRIMARY KEY",
|
||||
"AUTOINCREMENT",
|
||||
]
|
||||
.join(" ")
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
// data fields
|
||||
.concat(
|
||||
domain.data_fields
|
||||
.map(
|
||||
(data_field) => lib_plankton.string.coin(
|
||||
"`{{name}}` {{parameters}}",
|
||||
{
|
||||
"name": data_field.name,
|
||||
"parameters": (
|
||||
(
|
||||
// type
|
||||
[
|
||||
{
|
||||
"boolean": "BOOLEAN",
|
||||
"integer": "INTEGER",
|
||||
"string_short": "VARCHAR(63)",
|
||||
"string_medium": "VARCHAR(255)",
|
||||
"string_long": "TEXT",
|
||||
}[data_field.type],
|
||||
]
|
||||
// nullability
|
||||
.concat(
|
||||
data_field.nullable
|
||||
? []
|
||||
: ["NOT NULL"]
|
||||
)
|
||||
// default
|
||||
.concat(
|
||||
(data_field.default === undefined)
|
||||
? []
|
||||
: [
|
||||
lib_plankton.string.coin(
|
||||
"DEFAULT {{value}}",
|
||||
{
|
||||
"value": value_encode(data_field.default),
|
||||
}
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
.join(" ")
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
// constraints
|
||||
.concat(
|
||||
domain.constraints
|
||||
.map(
|
||||
(constraint) => {
|
||||
switch (constraint.kind) {
|
||||
default: {
|
||||
throw (new Error("unhandled constraint kind: " + constraint.kind));
|
||||
break;
|
||||
}
|
||||
case "foreign_key": {
|
||||
return lib_plankton.string.coin(
|
||||
"FOREIGN KEY ({{fields}}) REFERENCES `{{reference_name}}`({{reference_fields}})",
|
||||
{
|
||||
"fields": (
|
||||
constraint.parameters["fields"]
|
||||
.map(x => ('`' + x + '`'))
|
||||
.join(",")
|
||||
),
|
||||
"reference_name": constraint.parameters["reference"]["name"],
|
||||
"reference_fields": (
|
||||
constraint.parameters["reference"]["fields"]
|
||||
.map(x => ('`' + x + '`'))
|
||||
.join(",")
|
||||
),
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "unique": {
|
||||
return lib_plankton.string.coin(
|
||||
"UNIQUE ({{fields}})",
|
||||
{
|
||||
"fields": (
|
||||
constraint.parameters["fields"]
|
||||
.map(x => ('`' + x + '`'))
|
||||
.join(",")
|
||||
),
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(x => ("\t\t" + x))
|
||||
.join(",\n")
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
.map(x => (x + "\n"))
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_sindri.add_output(
|
||||
_sindri.enum_realm.database,
|
||||
"sqlite",
|
||||
{
|
||||
"render": (x) => Promise.resolve<string>(_sindri.outputs.database.sqlite.render(x)),
|
||||
}
|
||||
);
|
||||
37
source/outputs/frontend/typescript/logic.ts
Normal file
37
source/outputs/frontend/typescript/logic.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
namespace _sindri.outputs.frontend.typescript
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
async function get_template(
|
||||
name : string
|
||||
) : Promise<string>
|
||||
{
|
||||
return _sindri.get_template(_sindri.enum_realm.frontend, "typescript", name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function render(
|
||||
input_data
|
||||
) : Promise<string>
|
||||
{
|
||||
return lib_plankton.string.coin(
|
||||
await get_template("core.ts.tpl"),
|
||||
{
|
||||
"value": JSON.stringify(input_data, undefined, "\t"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
_sindri.add_output(
|
||||
_sindri.enum_realm.frontend,
|
||||
"typescript",
|
||||
{
|
||||
"render": _sindri.outputs.frontend.typescript.render,
|
||||
}
|
||||
);
|
||||
251
source/outputs/frontend/typescript/templates/core.ts.tpl
Normal file
251
source/outputs/frontend/typescript/templates/core.ts.tpl
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
namespace _sindri
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
const abstract = {{value}};
|
||||
|
||||
|
||||
/**
|
||||
* @todo headers
|
||||
* @todo query
|
||||
*/
|
||||
export function api_call(
|
||||
method : string,
|
||||
path : string,
|
||||
input : any = null
|
||||
) : Promise<any>
|
||||
{
|
||||
return (
|
||||
fetch(
|
||||
(conf.backend.scheme + "://" + conf.backend.host + ":" + conf.backend.port.toFixed() + path),
|
||||
{
|
||||
"method": method,
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"body": (
|
||||
(input === null)
|
||||
? undefined
|
||||
: /*Buffer.from*/(JSON.stringify(input))
|
||||
),
|
||||
}
|
||||
)
|
||||
.then(
|
||||
x => x.json()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function editor(
|
||||
domain_description : any,
|
||||
hook_switch : (null | ((state : lib_plankton.zoo_editor.type_state<int, any>) => void))
|
||||
) : lib_plankton.zoo_editor.type_editor<int, any>
|
||||
{
|
||||
return lib_plankton.zoo_editor.make<int, any>(
|
||||
// store
|
||||
{
|
||||
"setup": () => Promise.resolve<void>(undefined),
|
||||
"search": (term) => (
|
||||
api_call(
|
||||
"GET",
|
||||
lib_plankton.string.coin(
|
||||
"/{{name}}",
|
||||
{
|
||||
"name": domain_description.name,
|
||||
}
|
||||
),
|
||||
null
|
||||
)
|
||||
.then(
|
||||
(entries) => Promise.resolve<Array<{key : int; preview : any}>>(
|
||||
entries
|
||||
.filter(
|
||||
entry => (
|
||||
entry.key.toFixed().includes(term.toLowerCase())
|
||||
||
|
||||
(
|
||||
/*
|
||||
(term.length >= 3)
|
||||
&&
|
||||
*/
|
||||
domain_description.data_fields
|
||||
.some(
|
||||
data_field => JSON.stringify(entry.value[data_field.name]).toLowerCase().includes(term.toLowerCase())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(
|
||||
entry => ({
|
||||
"key": entry.key,
|
||||
"preview": entry.value,
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
"read": (id) => api_call(
|
||||
"GET",
|
||||
lib_plankton.string.coin(
|
||||
"/{{name}}/{{id}}",
|
||||
{
|
||||
"name": domain_description.name,
|
||||
"id": id.toFixed(0),
|
||||
}
|
||||
),
|
||||
null
|
||||
),
|
||||
"create": (object) => api_call(
|
||||
"POST",
|
||||
lib_plankton.string.coin(
|
||||
"/{{name}}",
|
||||
{
|
||||
"name": domain_description.name,
|
||||
}
|
||||
),
|
||||
object
|
||||
),
|
||||
"update": (id, object) => api_call(
|
||||
"PATCH",
|
||||
lib_plankton.string.coin(
|
||||
"/{{name}}/{{id}}",
|
||||
{
|
||||
"name": domain_description.name,
|
||||
"id": id.toFixed(0),
|
||||
}
|
||||
),
|
||||
object
|
||||
),
|
||||
"delete": (id) => api_call(
|
||||
"DELETE",
|
||||
lib_plankton.string.coin(
|
||||
"/{{name}}/{{id}}",
|
||||
{
|
||||
"name": domain_description.name,
|
||||
"id": id.toFixed(0),
|
||||
}
|
||||
),
|
||||
null
|
||||
),
|
||||
},
|
||||
// form
|
||||
lib_plankton.zoo_form.make<any>(
|
||||
// method
|
||||
"GET",
|
||||
// fields
|
||||
(
|
||||
domain_description.data_fields.map(
|
||||
data_field => ({
|
||||
"name": data_field.name,
|
||||
"type": {
|
||||
"string_short": "text",
|
||||
"string_medium": "text",
|
||||
"string_long": "text",
|
||||
"integer": "number",
|
||||
"float": "number",
|
||||
}[data_field.type],
|
||||
})
|
||||
)
|
||||
),
|
||||
// encode
|
||||
(object) => object,
|
||||
// decode
|
||||
(object_encoded) => object_encoded,
|
||||
),
|
||||
// options
|
||||
{
|
||||
"hook_switch": hook_switch,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function edit_location_name(
|
||||
domain_description
|
||||
) : string
|
||||
{
|
||||
return lib_plankton.string.coin(
|
||||
"edit_{{name}}",
|
||||
{
|
||||
"name": domain_description.name,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function register_editor_page_for_domain(
|
||||
domain_description
|
||||
) : void
|
||||
{
|
||||
const location_name : string = edit_location_name(domain_description);
|
||||
lib_plankton.zoo_page.register(
|
||||
location_name,
|
||||
async (parameters, element_main) => {
|
||||
const id : (null | int) = ((! ("id" in parameters)) ? null : parseInt(parameters["id"]));
|
||||
const mode : lib_plankton.zoo_editor.enum_mode = editor_decode_mode(parameters["mode"], id);
|
||||
const search_term : (null | string) = (parameters["search"] ?? null);
|
||||
lib_plankton.zoo_editor.render<int, any>(
|
||||
editor(
|
||||
domain_description,
|
||||
(state) => {
|
||||
lib_plankton.zoo_page.set(
|
||||
{
|
||||
"name": location_name,
|
||||
"parameters": {
|
||||
"mode": state.mode,
|
||||
"id": ((state.key === null) ? null : state.key.toFixed(0)),
|
||||
"search": state.search_state.term,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
),
|
||||
element_main,
|
||||
{
|
||||
"state": {
|
||||
"mode": mode,
|
||||
"key": id,
|
||||
"search_state": {"term": search_term},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
lib_plankton.zoo_page.add_nav_entry(
|
||||
{
|
||||
"name": location_name,
|
||||
"parameters": {
|
||||
"mode": "find",
|
||||
"key": null,
|
||||
"search": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": (domain_description.name + "s"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function init(
|
||||
) : Promise<void>
|
||||
{
|
||||
abstract.domains.forEach(
|
||||
domain_description => {
|
||||
register_editor_page_for_domain(domain_description);
|
||||
}
|
||||
);
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
}
|
||||
103
source/outputs/other/jsonschema/logic.ts
Normal file
103
source/outputs/other/jsonschema/logic.ts
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
namespace _sindri.outputs.other.jsonschema
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export function render(
|
||||
input_data
|
||||
) : string
|
||||
{
|
||||
return lib_plankton.json.encode(
|
||||
Object.fromEntries(
|
||||
input_data.domains.map(
|
||||
domain => ([
|
||||
domain.name,
|
||||
{
|
||||
"type": ["array"],
|
||||
"items": {
|
||||
"type": ["object"],
|
||||
"additionalProperties": false,
|
||||
"properties": Object.fromEntries(
|
||||
[]
|
||||
.concat(
|
||||
(domain.key_field === null)
|
||||
? []
|
||||
: [
|
||||
[
|
||||
domain.key_field.name,
|
||||
{
|
||||
"type": ["integer"],
|
||||
"description": (domain.key_field.description ?? undefined),
|
||||
}
|
||||
]
|
||||
]
|
||||
)
|
||||
.concat(
|
||||
domain.data_fields
|
||||
.map(
|
||||
data_field => ([
|
||||
data_field.name,
|
||||
{
|
||||
"type": (
|
||||
[]
|
||||
.concat(
|
||||
data_field.nullable
|
||||
? ["null"]
|
||||
: []
|
||||
)
|
||||
.concat(
|
||||
[
|
||||
{
|
||||
"boolean": "boolean",
|
||||
"integer": "integer",
|
||||
"float": "number",
|
||||
"string_short": "string",
|
||||
"string_medium": "string",
|
||||
"string_long": "string",
|
||||
}[data_field.type]
|
||||
]
|
||||
)
|
||||
),
|
||||
"description": (data_field.description ?? undefined),
|
||||
}
|
||||
])
|
||||
)
|
||||
)
|
||||
),
|
||||
"required": (
|
||||
[]
|
||||
.concat(
|
||||
(domain.key_field === null)
|
||||
? []
|
||||
: [domain.key_field.name]
|
||||
)
|
||||
.concat(
|
||||
domain.data_fields
|
||||
.map(
|
||||
data_field => data_field.name
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
}
|
||||
])
|
||||
)
|
||||
),
|
||||
{
|
||||
"formatted": true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
_sindri.add_output(
|
||||
_sindri.enum_realm.other,
|
||||
"jsonschema",
|
||||
{
|
||||
"render": (x) => Promise.resolve<string>(_sindri.outputs.other.jsonschema.render(x)),
|
||||
}
|
||||
);
|
||||
4
tools/build
Executable file
4
tools/build
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
make --file=tools/makefile
|
||||
|
||||
3
tools/clear
Executable file
3
tools/clear
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
rm --recursive --force temp build
|
||||
15
tools/get-plankton
Executable file
15
tools/get-plankton
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
modules=""
|
||||
modules="${modules} base"
|
||||
# modules="${modules} call"
|
||||
modules="${modules} string"
|
||||
modules="${modules} json"
|
||||
modules="${modules} file"
|
||||
modules="${modules} prog"
|
||||
modules="${modules} args"
|
||||
|
||||
mkdir -p lib/plankton
|
||||
cd lib/plankton
|
||||
ptk bundle node ${modules}
|
||||
cd -
|
||||
3
tools/install
Executable file
3
tools/install
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
cp build/sindri /usr/local/bin/sindri
|
||||
44
tools/makefile
Normal file
44
tools/makefile
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
## commands
|
||||
|
||||
cmd_create_directory := mkdir --parents
|
||||
cmd_typescript_compile := tsc
|
||||
cmd_concatenate := cat
|
||||
cmd_chmod := chmod
|
||||
cmd_echo := echo -e
|
||||
cmd_echox := echo
|
||||
cmd_log := echo -e "--"
|
||||
cmd_copy := cp -r -u -v
|
||||
|
||||
|
||||
## rules
|
||||
|
||||
.PHONY: all
|
||||
all: build/sindri templates
|
||||
|
||||
temp/sindri-unlinked.js: \
|
||||
lib/plankton/plankton.d.ts \
|
||||
source/base.ts \
|
||||
source/outputs/other/jsonschema/logic.ts \
|
||||
source/outputs/database/sqlite/logic.ts \
|
||||
source/outputs/database/mysql/logic.ts \
|
||||
source/outputs/database/postgresql/logic.ts \
|
||||
source/outputs/backend/typescript/logic.ts \
|
||||
source/outputs/frontend/typescript/logic.ts \
|
||||
source/conf.ts \
|
||||
source/main.ts
|
||||
@ ${cmd_log} "compiling …"
|
||||
@ ${cmd_create_directory} temp
|
||||
@ ${cmd_typescript_compile} $^ --lib es2020 --target es6 --outFile $@
|
||||
|
||||
build/sindri: lib/plankton/plankton.js temp/sindri-unlinked.js
|
||||
@ ${cmd_log} "linking …"
|
||||
@ ${cmd_create_directory} build
|
||||
@ ${cmd_echox} "#!/usr/bin/env node" > temp/head.js
|
||||
@ ${cmd_concatenate} temp/head.js $^ > $@
|
||||
@ ${cmd_chmod} +x $@
|
||||
|
||||
.PHONY: templates
|
||||
templates: \
|
||||
source/outputs/frontend/typescript/templates/core.ts.tpl
|
||||
@ ${cmd_log} "placing templates …"
|
||||
@ tools/place-templates
|
||||
17
tools/place-templates
Executable file
17
tools/place-templates
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
## consts
|
||||
|
||||
dir_from=source/outputs
|
||||
dir_to=build/templates
|
||||
|
||||
|
||||
## exec
|
||||
|
||||
paths=$(find ${dir_from} -type d -name templates)
|
||||
for path in ${paths}
|
||||
do
|
||||
type=$(echo ${path} | cut --delimiter='/' --fields='3-' | cut --delimiter='/' --fields='-2')
|
||||
mkdir --parents ${dir_to}/${type}
|
||||
cp --update --verbose ${path}/* ${dir_to}/${type}/
|
||||
done
|
||||
Loading…
Reference in a new issue