create-harper 0.6.0 → 0.7.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/index.js +11 -24
- package/lib/constants/exampleFiles.js +2 -2
- package/lib/constants/helpMessage.js +6 -1
- package/lib/steps/getEnvVars.js +5 -4
- package/lib/steps/parseArgv.js +54 -0
- package/package.json +4 -2
- package/template-react/README.md +2 -2
- package/template-react/resources/{exampleTable.js → examplePeople.js} +1 -1
- package/template-react/resources/exampleSocket.js +3 -3
- package/{template-react-ts/schemas/exampleTable.graphql → template-react/schemas/examplePeople.graphql} +1 -1
- package/template-react-ts/README.md +2 -2
- package/template-react-ts/resources/{exampleTable.ts → examplePeople.ts} +4 -4
- package/template-react-ts/resources/exampleSocket.ts +4 -4
- package/{template-vanilla-ts/schemas/exampleTable.graphql → template-react-ts/schemas/examplePeople.graphql} +1 -1
- package/template-vanilla/README.md +2 -2
- package/template-vanilla/resources/{exampleTable.js → examplePeople.js} +1 -1
- package/template-vanilla/resources/exampleSocket.js +3 -3
- package/{template-react/schemas/exampleTable.graphql → template-vanilla/schemas/examplePeople.graphql} +1 -1
- package/template-vanilla-ts/README.md +2 -2
- package/template-vanilla-ts/resources/{exampleTable.ts → examplePeople.ts} +4 -4
- package/template-vanilla-ts/resources/exampleSocket.ts +4 -4
- package/{template-vanilla/schemas/exampleTable.graphql → template-vanilla-ts/schemas/examplePeople.graphql} +1 -1
package/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as prompts from '@clack/prompts';
|
|
3
|
-
import mri from 'mri';
|
|
4
3
|
import { helpMessage } from './lib/constants/helpMessage.js';
|
|
5
|
-
import { formatTargetDir } from './lib/fs/formatTargetDir.js';
|
|
6
4
|
import { pkgFromUserAgent } from './lib/pkg/pkgFromUserAgent.js';
|
|
7
5
|
import { checkForUpdate } from './lib/steps/checkForUpdate.js';
|
|
8
6
|
import { getEnvVars } from './lib/steps/getEnvVars.js';
|
|
@@ -13,40 +11,29 @@ import { getRunAppImmediately } from './lib/steps/getRunAppImmediately.js';
|
|
|
13
11
|
import { getTemplate } from './lib/steps/getTemplate.js';
|
|
14
12
|
import { handleExistingDir } from './lib/steps/handleExistingDir.js';
|
|
15
13
|
import { helpAgents } from './lib/steps/helpAgents.js';
|
|
14
|
+
import { parseArgv } from './lib/steps/parseArgv.js';
|
|
16
15
|
import { scaffoldProject } from './lib/steps/scaffoldProject.js';
|
|
17
16
|
import { showOutro } from './lib/steps/showOutro.js';
|
|
18
17
|
|
|
19
|
-
const argv = mri(process.argv.slice(2), {
|
|
20
|
-
boolean: ['help', 'overwrite', 'immediate', 'interactive'],
|
|
21
|
-
alias: { h: 'help', t: 'template', i: 'immediate' },
|
|
22
|
-
string: ['template', 'cli-target-username', 'cli-target'],
|
|
23
|
-
});
|
|
24
|
-
|
|
25
18
|
init().catch((e) => {
|
|
26
19
|
console.error(e);
|
|
27
20
|
});
|
|
28
21
|
|
|
29
22
|
async function init() {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const argImmediate = argv.immediate;
|
|
34
|
-
const argInteractive = argv.interactive;
|
|
35
|
-
|
|
36
|
-
const help = argv.help;
|
|
37
|
-
if (help) {
|
|
23
|
+
const args = parseArgv(process.argv.slice(2));
|
|
24
|
+
|
|
25
|
+
if (args.help) {
|
|
38
26
|
console.log(helpMessage);
|
|
39
27
|
return;
|
|
40
28
|
}
|
|
41
29
|
|
|
42
30
|
const currentVersion = await checkForUpdate();
|
|
43
|
-
|
|
44
|
-
if (version) {
|
|
31
|
+
if (args.version) {
|
|
45
32
|
console.log(`Current version: ${currentVersion}`);
|
|
46
33
|
return;
|
|
47
34
|
}
|
|
48
35
|
|
|
49
|
-
const interactive =
|
|
36
|
+
const interactive = args.interactive;
|
|
50
37
|
|
|
51
38
|
// Detect AI agent environment for better agent experience (AX)
|
|
52
39
|
await helpAgents(interactive);
|
|
@@ -54,12 +41,12 @@ async function init() {
|
|
|
54
41
|
const cancel = () => prompts.cancel('Operation cancelled');
|
|
55
42
|
|
|
56
43
|
// Get the project name and target directory
|
|
57
|
-
const projectNameResult = await getProjectName(
|
|
44
|
+
const projectNameResult = await getProjectName(args.targetDir, interactive);
|
|
58
45
|
if (projectNameResult.cancelled) { return cancel(); }
|
|
59
46
|
const { projectName, targetDir } = projectNameResult;
|
|
60
47
|
|
|
61
48
|
// Handle if the directory exists and isn't empty
|
|
62
|
-
const handleExistingDirResult = await handleExistingDir(targetDir,
|
|
49
|
+
const handleExistingDirResult = await handleExistingDir(targetDir, args.overwrite, interactive);
|
|
63
50
|
if (handleExistingDirResult.cancelled) { return cancel(); }
|
|
64
51
|
|
|
65
52
|
// Get the package name
|
|
@@ -68,7 +55,7 @@ async function init() {
|
|
|
68
55
|
const { packageName } = packageNameResult;
|
|
69
56
|
|
|
70
57
|
// Choose a framework and variant
|
|
71
|
-
const templateResult = await getTemplate(
|
|
58
|
+
const templateResult = await getTemplate(args.template, interactive);
|
|
72
59
|
if (templateResult.cancelled) { return cancel(); }
|
|
73
60
|
const { template } = templateResult;
|
|
74
61
|
|
|
@@ -78,14 +65,14 @@ async function init() {
|
|
|
78
65
|
const { excludedFiles } = examplesResult;
|
|
79
66
|
|
|
80
67
|
// Get environment variables for .env file
|
|
81
|
-
const envVarsResult = await getEnvVars(
|
|
68
|
+
const envVarsResult = await getEnvVars(interactive, template, args.deploymentUsername, args.deploymentURL);
|
|
82
69
|
if (envVarsResult.cancelled) { return cancel(); }
|
|
83
70
|
const { envVars } = envVarsResult;
|
|
84
71
|
|
|
85
72
|
// Should we do a package manager installation?
|
|
86
73
|
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
|
|
87
74
|
const pkgManager = pkgInfo ? pkgInfo.name : 'npm';
|
|
88
|
-
const immediateResult = await getRunAppImmediately(
|
|
75
|
+
const immediateResult = await getRunAppImmediately(args.immediate, interactive, pkgManager);
|
|
89
76
|
if (immediateResult.cancelled) { return cancel(); }
|
|
90
77
|
const { immediate } = immediateResult;
|
|
91
78
|
|
|
@@ -13,12 +13,12 @@ export const exampleFiles = [
|
|
|
13
13
|
{
|
|
14
14
|
label: 'Table schema',
|
|
15
15
|
value: 'tableSchema',
|
|
16
|
-
files: ['schemas/
|
|
16
|
+
files: ['schemas/examplePeople.graphql'],
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
label: 'Table handling',
|
|
20
20
|
value: 'table',
|
|
21
|
-
files: ['resources/
|
|
21
|
+
files: ['resources/examplePeople.ts', 'resources/examplePeople.js'],
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
label: 'Resource',
|
|
@@ -15,11 +15,16 @@ Usage: create-harper [OPTION]... [DIRECTORY]
|
|
|
15
15
|
Create a new Harper project in JavaScript or TypeScript.
|
|
16
16
|
When running in TTY, the CLI will start in interactive mode.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
All options are optional:
|
|
19
19
|
-t, --template NAME use a specific template
|
|
20
20
|
-i, --immediate install dependencies and start dev
|
|
21
21
|
--interactive / --no-interactive force interactive / non-interactive mode
|
|
22
22
|
--version print out the version of the create-harper templates
|
|
23
|
+
--overwrite if a directory already exists, clear it out before applying the new template
|
|
24
|
+
|
|
25
|
+
If you are going to deploy your application to https://fabric.harper.fast/, you can choose to specify your:
|
|
26
|
+
--deploymentURL The https://fabric.harper.fast/ URL where you will host your application
|
|
27
|
+
--deploymentUsername The cluster username in https://fabric.harper.fast/ for your application
|
|
23
28
|
|
|
24
29
|
Available templates:
|
|
25
30
|
${yellow('vanilla-ts vanilla')}
|
package/lib/steps/getEnvVars.js
CHANGED
|
@@ -20,12 +20,13 @@ const {
|
|
|
20
20
|
/**
|
|
21
21
|
* Step 5: Get environment variables for the .env file, optionally prompting the user.
|
|
22
22
|
*
|
|
23
|
-
* @param {Record<string, any>} argv - The parsed CLI arguments.
|
|
24
23
|
* @param {boolean} interactive - Whether the CLI is running in interactive mode.
|
|
25
24
|
* @param {string} template - The selected template name.
|
|
25
|
+
* @param {string} [argDeploymentUsername] - The deployment username specified in the command line args.
|
|
26
|
+
* @param {string} [argDeploymentURL] - The deployment URL specified in the command line args.
|
|
26
27
|
* @returns {Promise<{envVars: EnvVars, cancelled: boolean}>} - The environment variables and cancellation status.
|
|
27
28
|
*/
|
|
28
|
-
export async function getEnvVars(
|
|
29
|
+
export async function getEnvVars(interactive, template, argDeploymentUsername, argDeploymentURL) {
|
|
29
30
|
const templateDir = path.resolve(
|
|
30
31
|
fileURLToPath(import.meta.url),
|
|
31
32
|
'..',
|
|
@@ -42,8 +43,8 @@ export async function getEnvVars(argv, interactive, template) {
|
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
let username =
|
|
46
|
-
let target =
|
|
46
|
+
let username = argDeploymentUsername;
|
|
47
|
+
let target = argDeploymentURL;
|
|
47
48
|
let password = '';
|
|
48
49
|
|
|
49
50
|
const prefix = gray('https://') + magenta('fabric.harper.fast') + gray('/') + ' Cluster ';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import mri from 'mri';
|
|
2
|
+
import { formatTargetDir } from '../fs/formatTargetDir.js';
|
|
3
|
+
|
|
4
|
+
export function parseArgv(rawArgv) {
|
|
5
|
+
const argv = mri(rawArgv, {
|
|
6
|
+
boolean: [
|
|
7
|
+
'help',
|
|
8
|
+
'immediate',
|
|
9
|
+
'interactive',
|
|
10
|
+
'overwrite',
|
|
11
|
+
'version',
|
|
12
|
+
],
|
|
13
|
+
string: [
|
|
14
|
+
'deploymentURL',
|
|
15
|
+
'deploymentUsername',
|
|
16
|
+
'template',
|
|
17
|
+
],
|
|
18
|
+
alias: {
|
|
19
|
+
h: 'help',
|
|
20
|
+
i: 'immediate',
|
|
21
|
+
t: 'template',
|
|
22
|
+
v: 'version',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const positionalArgs = [];
|
|
27
|
+
for (let i = 0; i < argv._.length; i++) {
|
|
28
|
+
const arg = argv._[i];
|
|
29
|
+
if (arg === 'help') {
|
|
30
|
+
argv.help = true;
|
|
31
|
+
} else if (arg === 'version' || arg === 'v') {
|
|
32
|
+
argv.version = true;
|
|
33
|
+
} else if (arg === 'template' || arg === 't') {
|
|
34
|
+
if (argv._[i + 1]) {
|
|
35
|
+
argv.template = argv._[i + 1];
|
|
36
|
+
i++;
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
positionalArgs.push(arg);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
deploymentURL: argv.deploymentURL,
|
|
45
|
+
deploymentUsername: argv.deploymentUsername,
|
|
46
|
+
help: argv.help,
|
|
47
|
+
immediate: argv.immediate,
|
|
48
|
+
interactive: argv.interactive ?? process.stdin.isTTY,
|
|
49
|
+
overwrite: argv.overwrite,
|
|
50
|
+
targetDir: positionalArgs[0] ? formatTargetDir(String(positionalArgs[0])) : undefined,
|
|
51
|
+
template: argv.template,
|
|
52
|
+
version: argv.version,
|
|
53
|
+
};
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-harper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HarperDB",
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
"prepare": "husky",
|
|
30
30
|
"test": "vitest run",
|
|
31
31
|
"test:coverage": "vitest run --coverage",
|
|
32
|
-
"test:watch": "vitest"
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"templates:apply-shared-templates": "(cd templates-shared && node ./applySharedTemplates.js)",
|
|
34
|
+
"templates:build-studio-templates": "(cd templates-studio && node ./buildStudioTemplates.js)"
|
|
33
35
|
},
|
|
34
36
|
"engines": {
|
|
35
37
|
"node": "^20.19.0 || >=22.12.0"
|
package/template-react/README.md
CHANGED
|
@@ -22,9 +22,9 @@ npm run dev
|
|
|
22
22
|
|
|
23
23
|
### Define Your Schema
|
|
24
24
|
|
|
25
|
-
The [schemas/
|
|
25
|
+
The [schemas/examplePeople.graphql](./schemas/examplePeople.graphql) is an example table schema definition. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have.
|
|
26
26
|
|
|
27
|
-
Open your [schemas/
|
|
27
|
+
Open your [schemas/examplePeople.graphql](./schemas/examplePeople.graphql) to take a look at an example schema. You can add as many table definitions to a single schema file as you want. You can also create one file per schema.
|
|
28
28
|
|
|
29
29
|
These schemas are the heart of a great Harper app. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have. REST endpoints will get stood up for any table that you `@export`.
|
|
30
30
|
|
|
@@ -8,7 +8,7 @@ export class ExampleSocket extends Resource {
|
|
|
8
8
|
target,
|
|
9
9
|
incomingMessages,
|
|
10
10
|
) {
|
|
11
|
-
const subscription = await tables.
|
|
11
|
+
const subscription = await tables.ExamplePeople.subscribe(target);
|
|
12
12
|
if (!incomingMessages) {
|
|
13
13
|
// Server sent events, no incoming messages!
|
|
14
14
|
// Subscribe to changes to the table.
|
|
@@ -18,7 +18,7 @@ export class ExampleSocket extends Resource {
|
|
|
18
18
|
const { type, id, name, tag } = message;
|
|
19
19
|
switch (type) {
|
|
20
20
|
case 'get':
|
|
21
|
-
const loaded = await tables.
|
|
21
|
+
const loaded = await tables.ExamplePeople.get(id);
|
|
22
22
|
yield {
|
|
23
23
|
type: 'get',
|
|
24
24
|
id,
|
|
@@ -26,7 +26,7 @@ export class ExampleSocket extends Resource {
|
|
|
26
26
|
};
|
|
27
27
|
break;
|
|
28
28
|
case 'put':
|
|
29
|
-
await tables.
|
|
29
|
+
await tables.ExamplePeople.put(id, { name, tag });
|
|
30
30
|
break;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Here we can define any tables in our database. This example shows how we define a type as a table using
|
|
2
2
|
## the type name as the table name and specifying it is an "export" available in the REST and other external protocols.
|
|
3
|
-
type
|
|
3
|
+
type ExamplePeople @table @export {
|
|
4
4
|
id: ID @primaryKey # Here we define primary key (must be one)
|
|
5
5
|
name: String # we can define any other attributes here
|
|
6
6
|
tag: String @indexed # we can specify any attributes that should be indexed
|
|
@@ -24,9 +24,9 @@ TypeScript is supported at runtime in Node.js through [type stripping](https://n
|
|
|
24
24
|
|
|
25
25
|
### Define Your Schema
|
|
26
26
|
|
|
27
|
-
The [schemas/
|
|
27
|
+
The [schemas/examplePeople.graphql](./schemas/examplePeople.graphql) is an example table schema definition. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have.
|
|
28
28
|
|
|
29
|
-
Open your [schemas/
|
|
29
|
+
Open your [schemas/examplePeople.graphql](./schemas/examplePeople.graphql) to take a look at an example schema. You can add as many table definitions to a single schema file as you want. You can also create one file per schema.
|
|
30
30
|
|
|
31
31
|
These schemas are the heart of a great Harper app. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have. REST endpoints will get stood up for any table that you `@export`.
|
|
32
32
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { type RequestTargetOrId, tables } from 'harperdb';
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface ExamplePerson {
|
|
4
4
|
id: string;
|
|
5
5
|
name: string;
|
|
6
6
|
tag: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export class
|
|
9
|
+
export class ExamplePeople extends tables.ExamplePeople<ExamplePerson> {
|
|
10
10
|
// we can define our own custom POST handler
|
|
11
|
-
async post(target: RequestTargetOrId, newRecord: Omit<
|
|
11
|
+
async post(target: RequestTargetOrId, newRecord: Omit<ExamplePerson, 'id'>) {
|
|
12
12
|
// do something with the incoming content;
|
|
13
13
|
return super.post(target, newRecord);
|
|
14
14
|
}
|
|
15
15
|
// or custom GET handler
|
|
16
|
-
async get(target: RequestTargetOrId): Promise<
|
|
16
|
+
async get(target: RequestTargetOrId): Promise<ExamplePerson> {
|
|
17
17
|
// we can modify this resource before returning
|
|
18
18
|
return super.get(target);
|
|
19
19
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type IterableEventQueue, RequestTarget, Resource, tables
|
|
1
|
+
import { type IterableEventQueue, RequestTarget, Resource, tables } from 'harperdb';
|
|
2
2
|
|
|
3
3
|
interface ExampleSocketRecord {
|
|
4
4
|
id: string;
|
|
@@ -15,7 +15,7 @@ export class ExampleSocket extends Resource<ExampleSocketRecord> {
|
|
|
15
15
|
target: RequestTarget,
|
|
16
16
|
incomingMessages: IterableEventQueue<ExampleSocketRecord>,
|
|
17
17
|
): AsyncIterable<ExampleSocketRecord> {
|
|
18
|
-
const subscription = await tables.
|
|
18
|
+
const subscription = await tables.ExamplePeople.subscribe(target);
|
|
19
19
|
if (!incomingMessages) {
|
|
20
20
|
// Server sent events, no incoming messages!
|
|
21
21
|
// Subscribe to changes to the table.
|
|
@@ -25,7 +25,7 @@ export class ExampleSocket extends Resource<ExampleSocketRecord> {
|
|
|
25
25
|
const { type, id, name, tag } = message;
|
|
26
26
|
switch (type) {
|
|
27
27
|
case 'get':
|
|
28
|
-
const loaded = await tables.
|
|
28
|
+
const loaded = await tables.ExamplePeople.get(id);
|
|
29
29
|
yield {
|
|
30
30
|
type: 'get',
|
|
31
31
|
id,
|
|
@@ -33,7 +33,7 @@ export class ExampleSocket extends Resource<ExampleSocketRecord> {
|
|
|
33
33
|
};
|
|
34
34
|
break;
|
|
35
35
|
case 'put':
|
|
36
|
-
await tables.
|
|
36
|
+
await tables.ExamplePeople.put(id, { name, tag });
|
|
37
37
|
break;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Here we can define any tables in our database. This example shows how we define a type as a table using
|
|
2
2
|
## the type name as the table name and specifying it is an "export" available in the REST and other external protocols.
|
|
3
|
-
type
|
|
3
|
+
type ExamplePeople @table @export {
|
|
4
4
|
id: ID @primaryKey # Here we define primary key (must be one)
|
|
5
5
|
name: String # we can define any other attributes here
|
|
6
6
|
tag: String @indexed # we can specify any attributes that should be indexed
|
|
@@ -22,9 +22,9 @@ npm run dev
|
|
|
22
22
|
|
|
23
23
|
### Define Your Schema
|
|
24
24
|
|
|
25
|
-
The [schemas/
|
|
25
|
+
The [schemas/examplePeople.graphql](./schemas/examplePeople.graphql) is an example table schema definition. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have.
|
|
26
26
|
|
|
27
|
-
Open your [schemas/
|
|
27
|
+
Open your [schemas/examplePeople.graphql](./schemas/examplePeople.graphql) to take a look at an example schema. You can add as many table definitions to a single schema file as you want. You can also create one file per schema.
|
|
28
28
|
|
|
29
29
|
These schemas are the heart of a great Harper app. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have. REST endpoints will get stood up for any table that you `@export`.
|
|
30
30
|
|
|
@@ -8,7 +8,7 @@ export class ExampleSocket extends Resource {
|
|
|
8
8
|
target,
|
|
9
9
|
incomingMessages,
|
|
10
10
|
) {
|
|
11
|
-
const subscription = await tables.
|
|
11
|
+
const subscription = await tables.ExamplePeople.subscribe(target);
|
|
12
12
|
if (!incomingMessages) {
|
|
13
13
|
// Server sent events, no incoming messages!
|
|
14
14
|
// Subscribe to changes to the table.
|
|
@@ -18,7 +18,7 @@ export class ExampleSocket extends Resource {
|
|
|
18
18
|
const { type, id, name, tag } = message;
|
|
19
19
|
switch (type) {
|
|
20
20
|
case 'get':
|
|
21
|
-
const loaded = await tables.
|
|
21
|
+
const loaded = await tables.ExamplePeople.get(id);
|
|
22
22
|
yield {
|
|
23
23
|
type: 'get',
|
|
24
24
|
id,
|
|
@@ -26,7 +26,7 @@ export class ExampleSocket extends Resource {
|
|
|
26
26
|
};
|
|
27
27
|
break;
|
|
28
28
|
case 'put':
|
|
29
|
-
await tables.
|
|
29
|
+
await tables.ExamplePeople.put(id, { name, tag });
|
|
30
30
|
break;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Here we can define any tables in our database. This example shows how we define a type as a table using
|
|
2
2
|
## the type name as the table name and specifying it is an "export" available in the REST and other external protocols.
|
|
3
|
-
type
|
|
3
|
+
type ExamplePeople @table @export {
|
|
4
4
|
id: ID @primaryKey # Here we define primary key (must be one)
|
|
5
5
|
name: String # we can define any other attributes here
|
|
6
6
|
tag: String @indexed # we can specify any attributes that should be indexed
|
|
@@ -24,9 +24,9 @@ TypeScript is supported at runtime in Node.js through [type stripping](https://n
|
|
|
24
24
|
|
|
25
25
|
### Define Your Schema
|
|
26
26
|
|
|
27
|
-
The [schemas/
|
|
27
|
+
The [schemas/examplePeople.graphql](./schemas/examplePeople.graphql) is an example table schema definition. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have.
|
|
28
28
|
|
|
29
|
-
Open your [schemas/
|
|
29
|
+
Open your [schemas/examplePeople.graphql](./schemas/examplePeople.graphql) to take a look at an example schema. You can add as many table definitions to a single schema file as you want. You can also create one file per schema.
|
|
30
30
|
|
|
31
31
|
These schemas are the heart of a great Harper app. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have. REST endpoints will get stood up for any table that you `@export`.
|
|
32
32
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { type RequestTargetOrId, tables } from 'harperdb';
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface ExamplePerson {
|
|
4
4
|
id: string;
|
|
5
5
|
name: string;
|
|
6
6
|
tag: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export class
|
|
9
|
+
export class ExamplePeople extends tables.ExamplePeople<ExamplePerson> {
|
|
10
10
|
// we can define our own custom POST handler
|
|
11
|
-
async post(target: RequestTargetOrId, newRecord: Omit<
|
|
11
|
+
async post(target: RequestTargetOrId, newRecord: Omit<ExamplePerson, 'id'>) {
|
|
12
12
|
// do something with the incoming content;
|
|
13
13
|
return super.post(target, newRecord);
|
|
14
14
|
}
|
|
15
15
|
// or custom GET handler
|
|
16
|
-
async get(target: RequestTargetOrId): Promise<
|
|
16
|
+
async get(target: RequestTargetOrId): Promise<ExamplePerson> {
|
|
17
17
|
// we can modify this resource before returning
|
|
18
18
|
return super.get(target);
|
|
19
19
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type IterableEventQueue, RequestTarget, Resource, tables
|
|
1
|
+
import { type IterableEventQueue, RequestTarget, Resource, tables } from 'harperdb';
|
|
2
2
|
|
|
3
3
|
interface ExampleSocketRecord {
|
|
4
4
|
id: string;
|
|
@@ -15,7 +15,7 @@ export class ExampleSocket extends Resource<ExampleSocketRecord> {
|
|
|
15
15
|
target: RequestTarget,
|
|
16
16
|
incomingMessages: IterableEventQueue<ExampleSocketRecord>,
|
|
17
17
|
): AsyncIterable<ExampleSocketRecord> {
|
|
18
|
-
const subscription = await tables.
|
|
18
|
+
const subscription = await tables.ExamplePeople.subscribe(target);
|
|
19
19
|
if (!incomingMessages) {
|
|
20
20
|
// Server sent events, no incoming messages!
|
|
21
21
|
// Subscribe to changes to the table.
|
|
@@ -25,7 +25,7 @@ export class ExampleSocket extends Resource<ExampleSocketRecord> {
|
|
|
25
25
|
const { type, id, name, tag } = message;
|
|
26
26
|
switch (type) {
|
|
27
27
|
case 'get':
|
|
28
|
-
const loaded = await tables.
|
|
28
|
+
const loaded = await tables.ExamplePeople.get(id);
|
|
29
29
|
yield {
|
|
30
30
|
type: 'get',
|
|
31
31
|
id,
|
|
@@ -33,7 +33,7 @@ export class ExampleSocket extends Resource<ExampleSocketRecord> {
|
|
|
33
33
|
};
|
|
34
34
|
break;
|
|
35
35
|
case 'put':
|
|
36
|
-
await tables.
|
|
36
|
+
await tables.ExamplePeople.put(id, { name, tag });
|
|
37
37
|
break;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Here we can define any tables in our database. This example shows how we define a type as a table using
|
|
2
2
|
## the type name as the table name and specifying it is an "export" available in the REST and other external protocols.
|
|
3
|
-
type
|
|
3
|
+
type ExamplePeople @table @export {
|
|
4
4
|
id: ID @primaryKey # Here we define primary key (must be one)
|
|
5
5
|
name: String # we can define any other attributes here
|
|
6
6
|
tag: String @indexed # we can specify any attributes that should be indexed
|