pinstripe 0.7.0 → 0.11.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.
- package/README.md +1 -1
- package/cli.js +8 -3
- package/lib/async_path_builder.js +38 -39
- package/lib/async_path_builder.test.js +27 -0
- package/lib/base.js +51 -17
- package/lib/base.test.js +80 -0
- package/lib/client.js +49 -0
- package/lib/command.js +69 -0
- package/lib/commands/_importer.js +1 -3
- package/lib/commands/create_database.js +2 -0
- package/lib/commands/drop_database.js +2 -0
- package/lib/commands/generate_command.js +32 -0
- package/lib/commands/generate_migration.js +45 -0
- package/lib/commands/generate_model.js +44 -0
- package/lib/commands/generate_project.js +49 -0
- package/lib/commands/generate_service.js +32 -0
- package/lib/commands/generate_static_site.js +79 -0
- package/lib/commands/generate_view.js +34 -0
- package/lib/commands/generate_widget.js +36 -0
- package/lib/commands/init_database.js +6 -0
- package/lib/commands/list_commands.js +14 -0
- package/lib/commands/list_migrations.js +14 -0
- package/lib/commands/list_models.js +14 -0
- package/lib/commands/list_services.js +15 -0
- package/lib/commands/list_views.js +14 -0
- package/lib/commands/list_widgets.js +14 -0
- package/lib/commands/migrate_database.js +2 -0
- package/lib/commands/reset_database.js +5 -0
- package/lib/commands/seed_database.js +4 -0
- package/lib/commands/{start_console.server.js → start_console.js} +8 -7
- package/lib/commands/start_server.js +76 -0
- package/lib/constants.js +21 -0
- package/lib/database/column.js +94 -0
- package/lib/database/constants.js +84 -0
- package/lib/database/migration.js +74 -0
- package/lib/database/migrator.js +38 -0
- package/lib/database/row.js +278 -0
- package/lib/database/sql.js +75 -0
- package/lib/database/table.js +488 -0
- package/lib/database/union.js +180 -0
- package/lib/database.js +197 -0
- package/lib/environment.js +29 -25
- package/lib/event_wrapper.js +26 -25
- package/lib/hookable.js +21 -19
- package/lib/html.js +60 -58
- package/lib/import_all.js +17 -17
- package/lib/index.js +19 -10
- package/lib/inflector.js +162 -160
- package/lib/initialize.client.js +25 -23
- package/lib/migrations/1627976184_create_user.js +9 -0
- package/lib/migrations/1628057822_create_session.js +7 -0
- package/lib/migrations/_importer.js +2 -0
- package/lib/models/_importer.js +2 -0
- package/lib/models/session.js +9 -0
- package/lib/models/user.js +45 -0
- package/lib/node_wrapper.js +426 -246
- package/lib/overload.js +1 -0
- package/lib/project.js +36 -38
- package/lib/registrable.js +80 -42
- package/lib/service_factory.js +90 -47
- package/lib/services/_importer.js +2 -0
- package/lib/services/args.js +2 -0
- package/lib/services/cli_utils.js +71 -0
- package/lib/services/config.js +9 -0
- package/lib/services/cookies.js +20 -0
- package/lib/services/database.js +12 -0
- package/lib/services/fetch.js +99 -0
- package/lib/services/fork_environment.js +16 -0
- package/lib/services/fs_builder.js +130 -0
- package/lib/services/global.js +9 -0
- package/lib/services/params.js +2 -0
- package/lib/services/project.js +4 -0
- package/lib/services/read_file.js +6 -0
- package/lib/services/render_file.js +9 -0
- package/lib/services/render_form.js +223 -0
- package/lib/services/render_html.js +6 -0
- package/lib/services/render_list.js +58 -0
- package/lib/services/render_markdown.js +6 -0
- package/lib/services/render_table.js +68 -0
- package/lib/services/render_view.js +18 -0
- package/lib/services/reset_environment.js +19 -0
- package/lib/services/run_command.js +9 -0
- package/lib/services/session.js +9 -0
- package/lib/services/view.js +4 -0
- package/lib/string_reader.js +20 -22
- package/lib/thatify.js +6 -0
- package/lib/url.js +77 -76
- package/lib/url.test.js +5 -1
- package/lib/validatable.js +60 -48
- package/lib/view.js +121 -2
- package/lib/views/_importer.js +1 -3
- package/lib/views/_layout.js +51 -0
- package/lib/views/bundle.css +10384 -0
- package/lib/views/bundle.css.map +1 -0
- package/lib/views/bundle.js +37 -0
- package/lib/views/sign_in.js +44 -0
- package/lib/views/sign_out.js +15 -0
- package/lib/virtual_node.js +125 -174
- package/lib/widgets/_importer.js +1 -3
- package/lib/widgets/frame.client.js +124 -0
- package/lib/widgets/internal/actions.client.js +51 -0
- package/lib/widgets/internal/document.client.js +56 -0
- package/lib/widgets/internal/markdown_editor.client.js +26 -0
- package/lib/widgets/internal/overlay.client.js +43 -0
- package/lib/widgets/{progress_bar.js → internal/progress_bar.client.js} +10 -3
- package/lib/widgets/trigger.client.js +20 -0
- package/package.json +16 -11
- package/pinstripe.scss +2 -0
- package/lib/command.server.js +0 -34
- package/lib/commands/create_database.server.js +0 -6
- package/lib/commands/drop_database.server.js +0 -6
- package/lib/commands/generate_command.server.js +0 -42
- package/lib/commands/generate_controller.server.js +0 -48
- package/lib/commands/generate_migration.server.js +0 -50
- package/lib/commands/generate_model.server.js +0 -50
- package/lib/commands/generate_project.server.js +0 -62
- package/lib/commands/generate_service.server.js +0 -42
- package/lib/commands/generate_view.server.js +0 -49
- package/lib/commands/generate_widget.server.js +0 -38
- package/lib/commands/init_database.server.js +0 -7
- package/lib/commands/list_commands.server.js +0 -15
- package/lib/commands/list_controllers.server.js +0 -15
- package/lib/commands/list_migrations.server.js +0 -15
- package/lib/commands/list_models.server.js +0 -15
- package/lib/commands/list_services.server.js +0 -15
- package/lib/commands/list_views.server.js +0 -15
- package/lib/commands/list_widgets.server.js +0 -15
- package/lib/commands/migrate_database.server.js +0 -7
- package/lib/commands/reset_database.server.js +0 -8
- package/lib/commands/start_server.server.js +0 -48
- package/lib/controller.js +0 -6
- package/lib/controllers/bundle.server.js +0 -61
- package/lib/database/column.server.js +0 -100
- package/lib/database/migration.server.js +0 -35
- package/lib/database/migrator.server.js +0 -40
- package/lib/database/row.server.js +0 -173
- package/lib/database/sql.server.js +0 -73
- package/lib/database/table.server.js +0 -409
- package/lib/database/union.server.js +0 -84
- package/lib/database.server.js +0 -181
- package/lib/generate_css.js +0 -39
- package/lib/renderable.js +0 -43
- package/lib/service_factories/_importer.js +0 -4
- package/lib/service_factories/args.server.js +0 -6
- package/lib/service_factories/camelize.js +0 -8
- package/lib/service_factories/capitalize.js +0 -8
- package/lib/service_factories/cli_utils.server.js +0 -76
- package/lib/service_factories/config.js +0 -13
- package/lib/service_factories/dasherize.js +0 -8
- package/lib/service_factories/database.server.js +0 -9
- package/lib/service_factories/fetch.js +0 -87
- package/lib/service_factories/fork_environment.js +0 -13
- package/lib/service_factories/fs_builder.server.js +0 -132
- package/lib/service_factories/params.js +0 -6
- package/lib/service_factories/pascalize.js +0 -8
- package/lib/service_factories/pluralize.js +0 -8
- package/lib/service_factories/project.js +0 -8
- package/lib/service_factories/render_controller.js +0 -11
- package/lib/service_factories/render_html.js +0 -8
- package/lib/service_factories/render_view.js +0 -11
- package/lib/service_factories/reset_environment.js +0 -21
- package/lib/service_factories/run_command.server.js +0 -11
- package/lib/service_factories/singularize.js +0 -8
- package/lib/service_factories/snakeify.js +0 -8
- package/lib/service_factories/uncapitalize.js +0 -8
- package/lib/views/layout.js +0 -13
- package/lib/widgets/anchor.js +0 -48
- package/lib/widgets/document.js +0 -38
- package/lib/widgets/form.js +0 -58
- package/lib/widgets/frame.js +0 -69
- package/lib/widgets/input.js +0 -13
- package/lib/widgets/modal.js +0 -20
- package/lib/widgets/script.js +0 -7
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
export default async ({
|
|
3
|
+
cliUtils: { extractArg },
|
|
4
|
+
fsBuilder: { inProjectRootDir, generateFile, line, indent },
|
|
5
|
+
snakeify, camelize
|
|
6
|
+
}) => {
|
|
7
|
+
const name = extractArg('');
|
|
8
|
+
if(name == ''){
|
|
9
|
+
console.error('A service name must be given.');
|
|
10
|
+
process.exit();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
await inProjectRootDir(async () => {
|
|
14
|
+
|
|
15
|
+
await generateFile(`lib/services/_importer.js`, { skipIfExists: true }, () => {
|
|
16
|
+
line();
|
|
17
|
+
line(`export { serviceImporter as default } from 'pinstripe';`);
|
|
18
|
+
line();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await generateFile(`lib/services/${snakeify(name)}.js`, () => {
|
|
22
|
+
line();
|
|
23
|
+
line(`export default () => {`);
|
|
24
|
+
indent(() => {
|
|
25
|
+
line(`return 'Example ${camelize(name)} service'`);
|
|
26
|
+
});
|
|
27
|
+
line('};');
|
|
28
|
+
line();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
});
|
|
32
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
import { VirtualNode } from '../virtual_node.js';
|
|
3
|
+
import { Url } from '../url.js';
|
|
4
|
+
import { View } from '../view.js';
|
|
5
|
+
import { default as mimeTypes } from 'mime-types';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
|
|
9
|
+
async run(){
|
|
10
|
+
this.pages = {};
|
|
11
|
+
const paths = Object.keys(View.classes).filter(path => !path.match(/(^|\/)_/)).map(path => {
|
|
12
|
+
return `/${path.replace(/(^|\/)index$/, '')}`.replace(/^\/+/, '/');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
while(paths.length){
|
|
16
|
+
await this.crawlPage({ _path: paths.shift() });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const pages = Object.values(this.pages).filter(page => page.status == 200 && Object.keys(page.params).length == 1);
|
|
20
|
+
const { inProjectRootDir, generateDir, generateFile, echo } = this.fsBuilder;
|
|
21
|
+
|
|
22
|
+
await inProjectRootDir(async () => {
|
|
23
|
+
await generateDir('build/static', async () => {
|
|
24
|
+
while(pages.length){
|
|
25
|
+
const { params, headers } = pages.shift();
|
|
26
|
+
const contentType = headers['content-type'];
|
|
27
|
+
|
|
28
|
+
const path = params._path;
|
|
29
|
+
let filePath = path.replace(/^\//, '');
|
|
30
|
+
if(filePath.match(/(^|\/)$/)){
|
|
31
|
+
filePath = `${filePath}index`;
|
|
32
|
+
}
|
|
33
|
+
if(!filePath.match(/[^/]+\.[^/]+$/)){
|
|
34
|
+
filePath = `${filePath}.${mimeTypes.extension(contentType)}`
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const data = (await this.fetch({ _path: path }))[2];
|
|
38
|
+
|
|
39
|
+
await generateFile(filePath, () => {
|
|
40
|
+
echo(data.join(''));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
async crawlPage(params){
|
|
48
|
+
const hash = JSON.stringify(params);
|
|
49
|
+
if(this.pages[hash]) return;
|
|
50
|
+
const page = { params };
|
|
51
|
+
this.pages[hash] = page;
|
|
52
|
+
const [ status, headers, data ] = await this.fetch(params);
|
|
53
|
+
page.status = status;
|
|
54
|
+
page.headers = headers;
|
|
55
|
+
if(status != 200 || headers['content-type'] != 'text/html') return;
|
|
56
|
+
const html = data.join('');
|
|
57
|
+
const virtualDom = VirtualNode.fromString(html);
|
|
58
|
+
const urls = this.extractUrls(virtualDom);
|
|
59
|
+
while(urls.length){
|
|
60
|
+
const url = urls.shift();
|
|
61
|
+
await this.crawlPage({ ...url.params, _path: url.path });
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
extractUrls(virtualDom){
|
|
66
|
+
const out = [];
|
|
67
|
+
virtualDom.traverse(({ attributes }) => {
|
|
68
|
+
['src', 'href'].forEach(name => {
|
|
69
|
+
const value = attributes[name];
|
|
70
|
+
if(!value) return;
|
|
71
|
+
const url = Url.fromString(value, 'http://localhost/');
|
|
72
|
+
if(url.host != 'localhost') return;
|
|
73
|
+
out.push(url);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
export default async ({
|
|
3
|
+
cliUtils: { extractArg },
|
|
4
|
+
fsBuilder: { inProjectRootDir, generateFile, line, indent }
|
|
5
|
+
}) => {
|
|
6
|
+
let name = extractArg('').replace(/^\//, '');
|
|
7
|
+
if(name == ''){
|
|
8
|
+
console.error('A view name must be given.');
|
|
9
|
+
process.exit();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if(!name.match(/\.[^\/]+$/)){
|
|
13
|
+
name = `${name}.tpl.js`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
await inProjectRootDir(async () => {
|
|
17
|
+
|
|
18
|
+
await generateFile(`lib/views/_importer.js`, { skipIfExists: true }, () => {
|
|
19
|
+
line();
|
|
20
|
+
line(`export { viewImporter as default } from 'pinstripe';`);
|
|
21
|
+
line();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
await generateFile(`lib/views/${name}`, () => {
|
|
25
|
+
line();
|
|
26
|
+
line('export default ({ renderHtml, params }) => renderHtml(`');
|
|
27
|
+
indent(() => {
|
|
28
|
+
line(`<h1>${name} view<h1></h1>`);
|
|
29
|
+
});
|
|
30
|
+
line('`);');
|
|
31
|
+
line();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
export default async ({
|
|
3
|
+
cliUtils: { extractArg },
|
|
4
|
+
fsBuilder: { inProjectRootDir, generateFile, line, indent },
|
|
5
|
+
snakeify
|
|
6
|
+
}) => {
|
|
7
|
+
const name = snakeify(extractArg(''));
|
|
8
|
+
if(name == ''){
|
|
9
|
+
console.error('A widget name must be given.');
|
|
10
|
+
process.exit();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
await inProjectRootDir(async () => {
|
|
14
|
+
|
|
15
|
+
await generateFile(`lib/widgets/_importer.js`, { skipIfExists: true }, () => {
|
|
16
|
+
line();
|
|
17
|
+
line(`export { widgetImporter as default } from 'pinstripe';`);
|
|
18
|
+
line();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await generateFile(`lib/widgets/${name}.client.js`, () => {
|
|
22
|
+
line();
|
|
23
|
+
line(`export default {`);
|
|
24
|
+
indent(() => {
|
|
25
|
+
line(`initialize(...args){`);
|
|
26
|
+
indent(() => {
|
|
27
|
+
line(`this.constructor.parent.prototype.initialize.call(this, ...args);`);
|
|
28
|
+
});
|
|
29
|
+
line(`}`);
|
|
30
|
+
});
|
|
31
|
+
line('};');
|
|
32
|
+
line();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
});
|
|
36
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
import { Command } from '../command.js';
|
|
5
|
+
|
|
6
|
+
export default () => {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following commands are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
Object.keys(Command.classes).sort().forEach(commandName => {
|
|
11
|
+
console.log(` * ${chalk.green(commandName)}`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
import { Migration } from '../database/migration.js'
|
|
5
|
+
|
|
6
|
+
export default () => {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following migrations are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
Object.keys(Migration.classes).sort().forEach(migrationName => {
|
|
11
|
+
console.log(` * ${chalk.green(migrationName)}`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
import { Row } from '../database/row.js';
|
|
5
|
+
|
|
6
|
+
export default () => {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following models are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
Object.keys(Row.classes).sort().forEach(modelName => {
|
|
11
|
+
console.log(` * ${chalk.green(modelName)}`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
import { ServiceFactory } from '../service_factory.js';
|
|
5
|
+
|
|
6
|
+
export default () => {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following services are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
Object.keys(ServiceFactory.classes).sort().forEach(serviceName => {
|
|
11
|
+
console.log(` * ${chalk.green(serviceName)}`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
};
|
|
15
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
import { View } from '../view.js';
|
|
5
|
+
|
|
6
|
+
export default () => {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following views are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
Object.keys(View.classes).sort().forEach(viewName => {
|
|
11
|
+
console.log(` * ${chalk.green(viewName)}`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
import { NodeWrapper } from '../node_wrapper.js';
|
|
5
|
+
|
|
6
|
+
export default () => {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following widgets are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
Object.keys(NodeWrapper.classes).filter(widgetName => !NodeWrapper.classes[widgetName].isPrivate).sort().forEach(widgetName => {
|
|
11
|
+
console.log(` * ${chalk.green(widgetName)}`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
};
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import * as Repl from 'repl';
|
|
3
3
|
import * as vm from 'vm';
|
|
4
4
|
import * as util from 'util';
|
|
5
|
-
import { ServiceFactory } from 'pinstripe';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import { ServiceFactory } from '../service_factory.js';
|
|
7
|
+
|
|
8
|
+
export default async ({ environment, resetEnvironment }) => {
|
|
9
|
+
await new Promise(resolve => {
|
|
10
10
|
const repl = Repl.start({
|
|
11
11
|
prompt: 'pinstripe > ',
|
|
12
12
|
|
|
@@ -43,6 +43,7 @@ export default Class => Class.props({
|
|
|
43
43
|
})
|
|
44
44
|
);
|
|
45
45
|
|
|
46
|
-
repl.on('exit',
|
|
47
|
-
}
|
|
48
|
-
|
|
46
|
+
repl.on('exit', resolve);
|
|
47
|
+
});
|
|
48
|
+
await resetEnvironment();
|
|
49
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import Busboy from 'busboy';
|
|
4
|
+
import { default as qs} from 'qs';
|
|
5
|
+
const { parse: parseQueryString } = qs;
|
|
6
|
+
|
|
7
|
+
export default async ({ fetch }) => {
|
|
8
|
+
const host = process.env.HOST || '127.0.0.1';
|
|
9
|
+
const port = process.env.PORT || 3000;
|
|
10
|
+
|
|
11
|
+
http.createServer(async (request, response) => {
|
|
12
|
+
try {
|
|
13
|
+
const params = await extractParams(request);
|
|
14
|
+
const [ status, headers, body ] = await fetch(params);
|
|
15
|
+
response.statusCode = status
|
|
16
|
+
Object.keys(headers).forEach(
|
|
17
|
+
(name) => response.setHeader(name, headers[name])
|
|
18
|
+
)
|
|
19
|
+
body.forEach(chunk => response.write(chunk));
|
|
20
|
+
response.end();
|
|
21
|
+
} catch (e){
|
|
22
|
+
response.statusCode = 500;
|
|
23
|
+
response.setHeader('content-type', 'text/plain');
|
|
24
|
+
response.end((e.stack || e).toString());
|
|
25
|
+
}
|
|
26
|
+
console.log(`${request.method}: ${request.url} (${response.statusCode})`);
|
|
27
|
+
}).listen(port, host, () => {
|
|
28
|
+
console.log(`Pinstripe running at http://${host}:${port}/`)
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const extractParams = async (request) => {
|
|
33
|
+
const { method, url, headers } = request;
|
|
34
|
+
const matches = url.match(/^([^\?]+)\?(.*)$/);
|
|
35
|
+
const path = matches ? matches[1] : url;
|
|
36
|
+
const queryString = parseQueryString(matches ? matches[2] : "");
|
|
37
|
+
const body = method.match(/^POST|PUT|PATCH$/) ? await parseBody(request) : {};
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
...queryString,
|
|
41
|
+
...body,
|
|
42
|
+
_method: method,
|
|
43
|
+
_path: path,
|
|
44
|
+
_headers: headers
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const parseBody = (request) => new Promise((resolve) => {
|
|
49
|
+
const out = {};
|
|
50
|
+
const busboy = new Busboy({ headers: request.headers });
|
|
51
|
+
|
|
52
|
+
busboy.on('file', function(fieldname, file, filename, encoding, mimeType) {
|
|
53
|
+
const chunks = [];
|
|
54
|
+
|
|
55
|
+
file.on('data', chunk => {
|
|
56
|
+
chunks.push(Buffer.from(chunk));
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
file.on('end', () => {
|
|
60
|
+
out[fieldname] = {
|
|
61
|
+
filename,
|
|
62
|
+
encoding,
|
|
63
|
+
mimeType,
|
|
64
|
+
data: Buffer.concat(chunks)
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
busboy.on('field', (fieldname, value) => {
|
|
70
|
+
out[fieldname] = value
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
busboy.on('finish', () => resolve(out));
|
|
74
|
+
|
|
75
|
+
request.pipe(busboy);
|
|
76
|
+
});
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
export const SELF_CLOSING_TAGS = [
|
|
3
|
+
'area',
|
|
4
|
+
'base',
|
|
5
|
+
'br',
|
|
6
|
+
'embed',
|
|
7
|
+
'hr',
|
|
8
|
+
'iframe',
|
|
9
|
+
'img',
|
|
10
|
+
'input',
|
|
11
|
+
'link',
|
|
12
|
+
'meta',
|
|
13
|
+
'param',
|
|
14
|
+
'source',
|
|
15
|
+
'track'
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const TEXT_ONLY_TAGS = [
|
|
19
|
+
'script',
|
|
20
|
+
'style'
|
|
21
|
+
];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
|
|
2
|
+
import { Base } from "../base.js";
|
|
3
|
+
import { Sql } from './sql.js';
|
|
4
|
+
import { TYPE_TO_MYSQL_COLUMN_TYPE_MAP, TYPE_TO_DEFAULT_VALUE_MAP } from './constants.js';
|
|
5
|
+
|
|
6
|
+
export const Column = Base.extend().include({
|
|
7
|
+
initialize(table, name, type){
|
|
8
|
+
this._table = table;
|
|
9
|
+
this._name = name;
|
|
10
|
+
this._type = type;
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
get sql(){
|
|
14
|
+
return Sql.fromTemplate(this);
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
toSql(){
|
|
18
|
+
return this.sql`${this._table}.${Sql.escapeIdentifier(this._name)}`;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async type(){
|
|
22
|
+
if(!this._type){
|
|
23
|
+
this._type = (await this._table[this._name])._type;
|
|
24
|
+
}
|
|
25
|
+
return this._type;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
async exists(){
|
|
29
|
+
return (await this.type()) !== undefined;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async create(type = 'string', options = {}){
|
|
33
|
+
options = {
|
|
34
|
+
index: type == 'foreign_key',
|
|
35
|
+
default: TYPE_TO_DEFAULT_VALUE_MAP[type],
|
|
36
|
+
...options
|
|
37
|
+
};
|
|
38
|
+
const table = this._table;
|
|
39
|
+
const database = table._database;
|
|
40
|
+
|
|
41
|
+
await table.create();
|
|
42
|
+
|
|
43
|
+
if(!await this.exists()){
|
|
44
|
+
await database.run`
|
|
45
|
+
alter table ${table.constructor}
|
|
46
|
+
add column ${Sql.escapeIdentifier(this._name)} ${[TYPE_TO_MYSQL_COLUMN_TYPE_MAP[type]]}
|
|
47
|
+
${options.default !== undefined ? this.sql`default ${options.default}` : undefined}
|
|
48
|
+
`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if(options.index){
|
|
52
|
+
await database.run`
|
|
53
|
+
alter table ${table.constructor}
|
|
54
|
+
add index(${Sql.escapeIdentifier(this._name)})
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return this;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async drop(){
|
|
62
|
+
const table = this._table;
|
|
63
|
+
const database = table._database;
|
|
64
|
+
|
|
65
|
+
if(await this.exists()){
|
|
66
|
+
await database.run`
|
|
67
|
+
alter table ${table.constructor}
|
|
68
|
+
drop ${Sql.escapeIdentifier(this._name)}
|
|
69
|
+
`;
|
|
70
|
+
this._type = undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return this;
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
async __beforeInspect(){
|
|
77
|
+
const exists = await this.exists();
|
|
78
|
+
this._inspectInfo = {
|
|
79
|
+
exists,
|
|
80
|
+
...(() => {
|
|
81
|
+
if(exists){
|
|
82
|
+
return {
|
|
83
|
+
type: this._type
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {};
|
|
87
|
+
})()
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
__inspect(){
|
|
92
|
+
return `${this._name} (Column) ${JSON.stringify(this._inspectInfo, null, 2)}`;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
|
|
2
|
+
export const TYPE_TO_MYSQL_COLUMN_TYPE_MAP = {
|
|
3
|
+
primary_key: "int(11) unsigned",
|
|
4
|
+
alternate_key: "binary(16)",
|
|
5
|
+
foreign_key: "binary(16)",
|
|
6
|
+
binary: "longblob",
|
|
7
|
+
boolean: "enum('false','true')",
|
|
8
|
+
date: "date",
|
|
9
|
+
datetime: "datetime",
|
|
10
|
+
decimal: "decimal",
|
|
11
|
+
float: "float",
|
|
12
|
+
integer: "int(11)",
|
|
13
|
+
string: "varchar(255)",
|
|
14
|
+
text: "longtext",
|
|
15
|
+
time: "time",
|
|
16
|
+
timestamp: "datetime"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const ALLOWED_TABLE_ADAPTER_COLUMN_TYPES = [
|
|
20
|
+
'boolean',
|
|
21
|
+
'date',
|
|
22
|
+
'datetime',
|
|
23
|
+
'decimal',
|
|
24
|
+
'float',
|
|
25
|
+
'integer',
|
|
26
|
+
'string',
|
|
27
|
+
'time',
|
|
28
|
+
'timestamp'
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export const TYPE_TO_DEFAULT_VALUE_MAP = {
|
|
32
|
+
boolean: false,
|
|
33
|
+
decimal: 0,
|
|
34
|
+
float: 0,
|
|
35
|
+
integer: 0,
|
|
36
|
+
string: ''
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const MYSQL_COLUMN_TYPE_TO_TYPE_MAP = (() => {
|
|
40
|
+
const out = {};
|
|
41
|
+
Object.keys(TYPE_TO_MYSQL_COLUMN_TYPE_MAP).forEach(
|
|
42
|
+
key => out[TYPE_TO_MYSQL_COLUMN_TYPE_MAP[key]] = key
|
|
43
|
+
);
|
|
44
|
+
return out;
|
|
45
|
+
})();
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
export const COMPARISON_OPERATORS = {
|
|
49
|
+
Eq: (column, value) => column.sql`${column} = ${value}`,
|
|
50
|
+
Ne: (column, value) => column.sql`${column} != ${value}`,
|
|
51
|
+
Lt: (column, value) => column.sql`${column} < ${value}`,
|
|
52
|
+
Gt: (column, value) => column.sql`${column} > ${value}`,
|
|
53
|
+
Le: (column, value) => column.sql`${column} <= ${value}`,
|
|
54
|
+
Ge: (column, value) => column.sql`${column} >= ${value}`,
|
|
55
|
+
BeginsWith: (column, value) => column.sql`${column} like concat(${value}, '%')`,
|
|
56
|
+
EndsWith: (column, value) => column.sql`${column} like concat('%', ${value})`,
|
|
57
|
+
Contains: (column, value) => column.sql`${column} like concat('%', ${value}, '%')`
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const KEY_COMPARISON_OPERATORS = {
|
|
61
|
+
Eq: (column, value) => column.sql`${column} = uuid_to_bin(${value})`,
|
|
62
|
+
Ne: (column, value) => column.sql`${column} != uuid_to_bin(${value})`
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const COMPARISON_OPERATOR_METHOD_PATTERN = new RegExp(`^(.+)(${Object.keys(COMPARISON_OPERATORS).join('|')})$`);
|
|
66
|
+
export const KEY_COMPARISON_OPERATOR_METHOD_PATTERN = new RegExp(`^(id|.+Id)(${Object.keys(KEY_COMPARISON_OPERATORS).join('|')})$`);
|
|
67
|
+
|
|
68
|
+
export const COLUMN_TYPE_TO_FORM_FIELD_TYPE_MAP = {
|
|
69
|
+
primary_key: "hidden",
|
|
70
|
+
alternate_key: "hidden",
|
|
71
|
+
foreign_key: "hidden",
|
|
72
|
+
binary: "file",
|
|
73
|
+
boolean: "checkbox",
|
|
74
|
+
date: "date",
|
|
75
|
+
datetime: "datetime-local",
|
|
76
|
+
decimal: "number",
|
|
77
|
+
float: "number",
|
|
78
|
+
integer: "number",
|
|
79
|
+
string: "text",
|
|
80
|
+
text: "textarea",
|
|
81
|
+
time: "datetime-local",
|
|
82
|
+
timestamp: "datetime-local"
|
|
83
|
+
};
|
|
84
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { Base } from '../base.js';
|
|
4
|
+
import { Registrable } from '../registrable.js';
|
|
5
|
+
import { overload } from '../overload.js';
|
|
6
|
+
import { thatify } from '../thatify.js';
|
|
7
|
+
import { addFileToClient } from '../client.js';
|
|
8
|
+
|
|
9
|
+
export const Migration = Base.extend().include({
|
|
10
|
+
meta(){
|
|
11
|
+
this.include(Registrable);
|
|
12
|
+
|
|
13
|
+
const { register } = this;
|
|
14
|
+
|
|
15
|
+
this.assignProps({
|
|
16
|
+
register(name){
|
|
17
|
+
if(!name.match(/^\d+/)){
|
|
18
|
+
throw new Error(`Invalid migration name '${name}' - it must begin with a unix timestamp`);
|
|
19
|
+
}
|
|
20
|
+
return register.call(this, name);
|
|
21
|
+
},
|
|
22
|
+
get schemaVersion(){
|
|
23
|
+
const matches = this.name.match(/^\d+/)
|
|
24
|
+
if(matches){
|
|
25
|
+
return parseInt(matches[0]);
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
initialize(environment){
|
|
33
|
+
this.environment = environment;
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
migrate(){
|
|
37
|
+
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
__getMissing(name){
|
|
41
|
+
return this.environment[name];
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const defineMigration = overload({
|
|
46
|
+
['string, object'](name, include){
|
|
47
|
+
const abstract = include.abstract;
|
|
48
|
+
delete include.abstract;
|
|
49
|
+
Migration.register(name, abstract).include(include);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
['string, function'](name, fn){
|
|
53
|
+
defineMigration(name, { migrate: thatify(fn) });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export const migrationImporter = dirPath => {
|
|
58
|
+
return async filePath => {
|
|
59
|
+
const relativeFilePath = filePath.substr(dirPath.length).replace(/^\//, '');
|
|
60
|
+
|
|
61
|
+
if(filePath.match(/\.js$/)){
|
|
62
|
+
const relativeFilePathWithoutExtension = relativeFilePath.replace(/\.[^/]+$/, '');
|
|
63
|
+
if(relativeFilePathWithoutExtension == '_importer'){
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
addFileToClient(filePath);
|
|
67
|
+
const definition = await ( await import(filePath) ).default;
|
|
68
|
+
if(definition !== undefined){
|
|
69
|
+
defineMigration(relativeFilePathWithoutExtension, definition);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
};
|