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.
Files changed (173) hide show
  1. package/README.md +1 -1
  2. package/cli.js +8 -3
  3. package/lib/async_path_builder.js +38 -39
  4. package/lib/async_path_builder.test.js +27 -0
  5. package/lib/base.js +51 -17
  6. package/lib/base.test.js +80 -0
  7. package/lib/client.js +49 -0
  8. package/lib/command.js +69 -0
  9. package/lib/commands/_importer.js +1 -3
  10. package/lib/commands/create_database.js +2 -0
  11. package/lib/commands/drop_database.js +2 -0
  12. package/lib/commands/generate_command.js +32 -0
  13. package/lib/commands/generate_migration.js +45 -0
  14. package/lib/commands/generate_model.js +44 -0
  15. package/lib/commands/generate_project.js +49 -0
  16. package/lib/commands/generate_service.js +32 -0
  17. package/lib/commands/generate_static_site.js +79 -0
  18. package/lib/commands/generate_view.js +34 -0
  19. package/lib/commands/generate_widget.js +36 -0
  20. package/lib/commands/init_database.js +6 -0
  21. package/lib/commands/list_commands.js +14 -0
  22. package/lib/commands/list_migrations.js +14 -0
  23. package/lib/commands/list_models.js +14 -0
  24. package/lib/commands/list_services.js +15 -0
  25. package/lib/commands/list_views.js +14 -0
  26. package/lib/commands/list_widgets.js +14 -0
  27. package/lib/commands/migrate_database.js +2 -0
  28. package/lib/commands/reset_database.js +5 -0
  29. package/lib/commands/seed_database.js +4 -0
  30. package/lib/commands/{start_console.server.js → start_console.js} +8 -7
  31. package/lib/commands/start_server.js +76 -0
  32. package/lib/constants.js +21 -0
  33. package/lib/database/column.js +94 -0
  34. package/lib/database/constants.js +84 -0
  35. package/lib/database/migration.js +74 -0
  36. package/lib/database/migrator.js +38 -0
  37. package/lib/database/row.js +278 -0
  38. package/lib/database/sql.js +75 -0
  39. package/lib/database/table.js +488 -0
  40. package/lib/database/union.js +180 -0
  41. package/lib/database.js +197 -0
  42. package/lib/environment.js +29 -25
  43. package/lib/event_wrapper.js +26 -25
  44. package/lib/hookable.js +21 -19
  45. package/lib/html.js +60 -58
  46. package/lib/import_all.js +17 -17
  47. package/lib/index.js +19 -10
  48. package/lib/inflector.js +162 -160
  49. package/lib/initialize.client.js +25 -23
  50. package/lib/migrations/1627976184_create_user.js +9 -0
  51. package/lib/migrations/1628057822_create_session.js +7 -0
  52. package/lib/migrations/_importer.js +2 -0
  53. package/lib/models/_importer.js +2 -0
  54. package/lib/models/session.js +9 -0
  55. package/lib/models/user.js +45 -0
  56. package/lib/node_wrapper.js +426 -246
  57. package/lib/overload.js +1 -0
  58. package/lib/project.js +36 -38
  59. package/lib/registrable.js +80 -42
  60. package/lib/service_factory.js +90 -47
  61. package/lib/services/_importer.js +2 -0
  62. package/lib/services/args.js +2 -0
  63. package/lib/services/cli_utils.js +71 -0
  64. package/lib/services/config.js +9 -0
  65. package/lib/services/cookies.js +20 -0
  66. package/lib/services/database.js +12 -0
  67. package/lib/services/fetch.js +99 -0
  68. package/lib/services/fork_environment.js +16 -0
  69. package/lib/services/fs_builder.js +130 -0
  70. package/lib/services/global.js +9 -0
  71. package/lib/services/params.js +2 -0
  72. package/lib/services/project.js +4 -0
  73. package/lib/services/read_file.js +6 -0
  74. package/lib/services/render_file.js +9 -0
  75. package/lib/services/render_form.js +223 -0
  76. package/lib/services/render_html.js +6 -0
  77. package/lib/services/render_list.js +58 -0
  78. package/lib/services/render_markdown.js +6 -0
  79. package/lib/services/render_table.js +68 -0
  80. package/lib/services/render_view.js +18 -0
  81. package/lib/services/reset_environment.js +19 -0
  82. package/lib/services/run_command.js +9 -0
  83. package/lib/services/session.js +9 -0
  84. package/lib/services/view.js +4 -0
  85. package/lib/string_reader.js +20 -22
  86. package/lib/thatify.js +6 -0
  87. package/lib/url.js +77 -76
  88. package/lib/url.test.js +5 -1
  89. package/lib/validatable.js +60 -48
  90. package/lib/view.js +121 -2
  91. package/lib/views/_importer.js +1 -3
  92. package/lib/views/_layout.js +51 -0
  93. package/lib/views/bundle.css +10384 -0
  94. package/lib/views/bundle.css.map +1 -0
  95. package/lib/views/bundle.js +37 -0
  96. package/lib/views/sign_in.js +44 -0
  97. package/lib/views/sign_out.js +15 -0
  98. package/lib/virtual_node.js +125 -174
  99. package/lib/widgets/_importer.js +1 -3
  100. package/lib/widgets/frame.client.js +124 -0
  101. package/lib/widgets/internal/actions.client.js +51 -0
  102. package/lib/widgets/internal/document.client.js +56 -0
  103. package/lib/widgets/internal/markdown_editor.client.js +26 -0
  104. package/lib/widgets/internal/overlay.client.js +43 -0
  105. package/lib/widgets/{progress_bar.js → internal/progress_bar.client.js} +10 -3
  106. package/lib/widgets/trigger.client.js +20 -0
  107. package/package.json +16 -11
  108. package/pinstripe.scss +2 -0
  109. package/lib/command.server.js +0 -34
  110. package/lib/commands/create_database.server.js +0 -6
  111. package/lib/commands/drop_database.server.js +0 -6
  112. package/lib/commands/generate_command.server.js +0 -42
  113. package/lib/commands/generate_controller.server.js +0 -48
  114. package/lib/commands/generate_migration.server.js +0 -50
  115. package/lib/commands/generate_model.server.js +0 -50
  116. package/lib/commands/generate_project.server.js +0 -62
  117. package/lib/commands/generate_service.server.js +0 -42
  118. package/lib/commands/generate_view.server.js +0 -49
  119. package/lib/commands/generate_widget.server.js +0 -38
  120. package/lib/commands/init_database.server.js +0 -7
  121. package/lib/commands/list_commands.server.js +0 -15
  122. package/lib/commands/list_controllers.server.js +0 -15
  123. package/lib/commands/list_migrations.server.js +0 -15
  124. package/lib/commands/list_models.server.js +0 -15
  125. package/lib/commands/list_services.server.js +0 -15
  126. package/lib/commands/list_views.server.js +0 -15
  127. package/lib/commands/list_widgets.server.js +0 -15
  128. package/lib/commands/migrate_database.server.js +0 -7
  129. package/lib/commands/reset_database.server.js +0 -8
  130. package/lib/commands/start_server.server.js +0 -48
  131. package/lib/controller.js +0 -6
  132. package/lib/controllers/bundle.server.js +0 -61
  133. package/lib/database/column.server.js +0 -100
  134. package/lib/database/migration.server.js +0 -35
  135. package/lib/database/migrator.server.js +0 -40
  136. package/lib/database/row.server.js +0 -173
  137. package/lib/database/sql.server.js +0 -73
  138. package/lib/database/table.server.js +0 -409
  139. package/lib/database/union.server.js +0 -84
  140. package/lib/database.server.js +0 -181
  141. package/lib/generate_css.js +0 -39
  142. package/lib/renderable.js +0 -43
  143. package/lib/service_factories/_importer.js +0 -4
  144. package/lib/service_factories/args.server.js +0 -6
  145. package/lib/service_factories/camelize.js +0 -8
  146. package/lib/service_factories/capitalize.js +0 -8
  147. package/lib/service_factories/cli_utils.server.js +0 -76
  148. package/lib/service_factories/config.js +0 -13
  149. package/lib/service_factories/dasherize.js +0 -8
  150. package/lib/service_factories/database.server.js +0 -9
  151. package/lib/service_factories/fetch.js +0 -87
  152. package/lib/service_factories/fork_environment.js +0 -13
  153. package/lib/service_factories/fs_builder.server.js +0 -132
  154. package/lib/service_factories/params.js +0 -6
  155. package/lib/service_factories/pascalize.js +0 -8
  156. package/lib/service_factories/pluralize.js +0 -8
  157. package/lib/service_factories/project.js +0 -8
  158. package/lib/service_factories/render_controller.js +0 -11
  159. package/lib/service_factories/render_html.js +0 -8
  160. package/lib/service_factories/render_view.js +0 -11
  161. package/lib/service_factories/reset_environment.js +0 -21
  162. package/lib/service_factories/run_command.server.js +0 -11
  163. package/lib/service_factories/singularize.js +0 -8
  164. package/lib/service_factories/snakeify.js +0 -8
  165. package/lib/service_factories/uncapitalize.js +0 -8
  166. package/lib/views/layout.js +0 -13
  167. package/lib/widgets/anchor.js +0 -48
  168. package/lib/widgets/document.js +0 -38
  169. package/lib/widgets/form.js +0 -58
  170. package/lib/widgets/frame.js +0 -69
  171. package/lib/widgets/input.js +0 -13
  172. package/lib/widgets/modal.js +0 -20
  173. 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,6 @@
1
+
2
+ export default async ({ runCommand }) => {
3
+ await runCommand('create-database');
4
+ await runCommand('migrate-database');
5
+ await runCommand('seed-database');
6
+ };
@@ -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
+ };
@@ -0,0 +1,2 @@
1
+
2
+ export default ({ database }) => database.migrate();
@@ -0,0 +1,5 @@
1
+
2
+ export default async ({ runCommand }) => {
3
+ await runCommand('drop-database');
4
+ await runCommand('init-database');
5
+ };
@@ -0,0 +1,4 @@
1
+
2
+ export default () => {
3
+ // do nothing
4
+ };
@@ -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
- export default Class => Class.props({
8
- run(){
9
- const { environment, resetEnvironment } = this;
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', resetEnvironment);
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
+ });
@@ -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
+ };