mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-30 18:46:57 +00:00
Auto-merge for PR #868 via VersionBot
Add push command which starts a build on remote resin servers
This commit is contained in:
commit
22e74983b0
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file
|
|||||||
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## v7.4.0 - 2018-05-10
|
||||||
|
|
||||||
|
* Add push command which starts a build on remote resin servers #868 [Cameron Diver]
|
||||||
|
|
||||||
## v7.3.8 - 2018-05-03
|
## v7.3.8 - 2018-05-03
|
||||||
|
|
||||||
* Catch require errors and provide helpful instructions #874 [Tim Perry]
|
* Catch require errors and provide helpful instructions #874 [Tim Perry]
|
||||||
|
@ -37,3 +37,4 @@ module.exports =
|
|||||||
deploy: require('./deploy')
|
deploy: require('./deploy')
|
||||||
util: require('./util')
|
util: require('./util')
|
||||||
preload: require('./preload')
|
preload: require('./preload')
|
||||||
|
push: require('./push')
|
||||||
|
142
lib/actions/push.ts
Normal file
142
lib/actions/push.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016-2017 Resin.io
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CommandDefinition } from 'capitano';
|
||||||
|
import { ResinSDK } from 'resin-sdk';
|
||||||
|
import { stripIndent } from 'common-tags';
|
||||||
|
|
||||||
|
async function getAppOwner(sdk: ResinSDK, appName: string) {
|
||||||
|
const {
|
||||||
|
exitWithExpectedError,
|
||||||
|
selectFromList,
|
||||||
|
} = await import('../utils/patterns');
|
||||||
|
const _ = await import('lodash');
|
||||||
|
|
||||||
|
const applications = await sdk.models.application.getAll({
|
||||||
|
$expand: {
|
||||||
|
user: {
|
||||||
|
$select: ['username'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
$filter: {
|
||||||
|
app_name: appName,
|
||||||
|
},
|
||||||
|
$select: ['id'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (applications == null || applications.length === 0) {
|
||||||
|
exitWithExpectedError(
|
||||||
|
stripIndent`
|
||||||
|
No applications found with name: ${appName}.
|
||||||
|
|
||||||
|
This could mean that the application does not exist, or you do
|
||||||
|
not have the permissions required to access it.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applications.length === 1) {
|
||||||
|
return _.get(applications, '[0].user[0].username');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got more than one application with the same name it means that the
|
||||||
|
// user has access to a collab app with the same name as a personal app. We
|
||||||
|
// present a list to the user which shows the fully qualified application
|
||||||
|
// name (user/appname) and allows them to select
|
||||||
|
const entries = _.map(applications, app => {
|
||||||
|
const username = _.get(app, 'user[0].username');
|
||||||
|
return {
|
||||||
|
name: `${username}/${appName}`,
|
||||||
|
extra: username,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const selected = await selectFromList(
|
||||||
|
`${
|
||||||
|
entries.length
|
||||||
|
} applications found with that name, please select the application you would like to push to`,
|
||||||
|
entries,
|
||||||
|
);
|
||||||
|
|
||||||
|
return selected.extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const push: CommandDefinition<
|
||||||
|
{
|
||||||
|
application: string;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: string;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
signature: 'push <application>',
|
||||||
|
description: 'Start a remote build on the resin.io cloud build servers',
|
||||||
|
help: stripIndent`
|
||||||
|
This command can be used to start a build on the remote
|
||||||
|
resin.io cloud builders. The given source directory will be sent to the
|
||||||
|
resin.io builder, and the build will proceed. This can be used as a drop-in
|
||||||
|
replacement for git push to deploy.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ resin push myApp
|
||||||
|
$ resin push myApp --source <source directory>
|
||||||
|
$ resin push myApp -s <source directory>
|
||||||
|
`,
|
||||||
|
permission: 'user',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
signature: 'source',
|
||||||
|
alias: 's',
|
||||||
|
description:
|
||||||
|
'The source that should be sent to the resin builder to be built (defaults to the current directory)',
|
||||||
|
parameter: 'source',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
async action(params, options, done) {
|
||||||
|
const sdk = (await import('resin-sdk')).fromSharedOptions();
|
||||||
|
const Bluebird = await import('bluebird');
|
||||||
|
const remote = await import('../utils/remote-build');
|
||||||
|
const { exitWithExpectedError } = await import('../utils/patterns');
|
||||||
|
|
||||||
|
const app: string | null = params.application;
|
||||||
|
if (app == null) {
|
||||||
|
exitWithExpectedError('You must specify an application');
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = options.source || '.';
|
||||||
|
if (process.env.DEBUG) {
|
||||||
|
console.log(`[debug] Using ${source} as build source`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bluebird.join(
|
||||||
|
sdk.auth.getToken(),
|
||||||
|
sdk.settings.get('resinUrl'),
|
||||||
|
getAppOwner(sdk, app),
|
||||||
|
(token, baseUrl, owner) => {
|
||||||
|
const args = {
|
||||||
|
app,
|
||||||
|
owner,
|
||||||
|
source,
|
||||||
|
auth: token,
|
||||||
|
baseUrl,
|
||||||
|
sdk,
|
||||||
|
};
|
||||||
|
|
||||||
|
return remote.startRemoteBuild(args);
|
||||||
|
},
|
||||||
|
).nodeify(done);
|
||||||
|
},
|
||||||
|
};
|
@ -212,6 +212,9 @@ capitano.command(actions.internal.osInit)
|
|||||||
capitano.command(actions.build)
|
capitano.command(actions.build)
|
||||||
capitano.command(actions.deploy)
|
capitano.command(actions.deploy)
|
||||||
|
|
||||||
|
#------------ Push/remote builds -------
|
||||||
|
capitano.command(actions.push.push)
|
||||||
|
|
||||||
update.notify()
|
update.notify()
|
||||||
|
|
||||||
cli = capitano.parse(process.argv)
|
cli = capitano.parse(process.argv)
|
||||||
|
@ -102,7 +102,7 @@ toPosixPath = (systemPath) ->
|
|||||||
path = require('path')
|
path = require('path')
|
||||||
systemPath.replace(new RegExp('\\' + path.sep, 'g'), '/')
|
systemPath.replace(new RegExp('\\' + path.sep, 'g'), '/')
|
||||||
|
|
||||||
tarDirectory = (dir) ->
|
exports.tarDirectory = tarDirectory = (dir) ->
|
||||||
tar = require('tar-stream')
|
tar = require('tar-stream')
|
||||||
klaw = require('klaw')
|
klaw = require('klaw')
|
||||||
path = require('path')
|
path = require('path')
|
||||||
|
3
lib/utils/compose.d.ts
vendored
Normal file
3
lib/utils/compose.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import * as Stream from 'stream';
|
||||||
|
|
||||||
|
export function tarDirectory(source: string): Promise<Stream.Readable>;
|
@ -23,6 +23,11 @@ import chalk from 'chalk';
|
|||||||
import validation = require('./validation');
|
import validation = require('./validation');
|
||||||
import messages = require('./messages');
|
import messages = require('./messages');
|
||||||
|
|
||||||
|
export interface ListSelectionEntry {
|
||||||
|
name: string;
|
||||||
|
extra: any;
|
||||||
|
}
|
||||||
|
|
||||||
export function authenticate(options: {}): Promise<void> {
|
export function authenticate(options: {}): Promise<void> {
|
||||||
return form
|
return form
|
||||||
.run(
|
.run(
|
||||||
@ -245,6 +250,20 @@ export function inferOrSelectDevice(preferredUuid: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectFromList(
|
||||||
|
message: string,
|
||||||
|
selections: ListSelectionEntry[],
|
||||||
|
): Promise<ListSelectionEntry> {
|
||||||
|
return form.ask({
|
||||||
|
message,
|
||||||
|
type: 'list',
|
||||||
|
choices: _.map(selections, s => ({
|
||||||
|
name: s.name,
|
||||||
|
value: s,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function printErrorMessage(message: string) {
|
export function printErrorMessage(message: string) {
|
||||||
console.error(chalk.red(message));
|
console.error(chalk.red(message));
|
||||||
console.error(chalk.red(`\n${messages.getHelp}\n`));
|
console.error(chalk.red(`\n${messages.getHelp}\n`));
|
||||||
|
215
lib/utils/remote-build.ts
Normal file
215
lib/utils/remote-build.ts
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016-2017 Resin.io
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as JSONStream from 'JSONStream';
|
||||||
|
import * as request from 'request';
|
||||||
|
import { ResinSDK } from 'resin-sdk';
|
||||||
|
import * as Stream from 'stream';
|
||||||
|
|
||||||
|
import { tarDirectory } from './compose';
|
||||||
|
|
||||||
|
const DEBUG_MODE = !!process.env.DEBUG;
|
||||||
|
|
||||||
|
const CURSOR_METADATA_REGEX = /([a-z]+)([0-9]+)?/;
|
||||||
|
const TRIM_REGEX = /\n+$/;
|
||||||
|
|
||||||
|
export interface RemoteBuild {
|
||||||
|
app: string;
|
||||||
|
owner: string;
|
||||||
|
source: string;
|
||||||
|
auth: string;
|
||||||
|
baseUrl: string;
|
||||||
|
|
||||||
|
sdk: ResinSDK;
|
||||||
|
|
||||||
|
// For internal use
|
||||||
|
releaseId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BuilderMessage {
|
||||||
|
message: string;
|
||||||
|
type?: string;
|
||||||
|
replace?: boolean;
|
||||||
|
// These will be set when the type === 'metadata'
|
||||||
|
resource?: string;
|
||||||
|
value?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getBuilderEndpoint(
|
||||||
|
baseUrl: string,
|
||||||
|
owner: string,
|
||||||
|
app: string,
|
||||||
|
): Promise<string> {
|
||||||
|
const querystring = await import('querystring');
|
||||||
|
const args = querystring.stringify({ owner, app });
|
||||||
|
return `https://builder.${baseUrl}/v3/build?${args}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startRemoteBuild(build: RemoteBuild): Promise<void> {
|
||||||
|
const Bluebird = await import('bluebird');
|
||||||
|
|
||||||
|
return new Bluebird(async (resolve, reject) => {
|
||||||
|
const stream = await getRequestStream(build);
|
||||||
|
|
||||||
|
// Special windows handling (win64 also reports win32)
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
const readline = (await import('readline')).createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
});
|
||||||
|
|
||||||
|
readline.on('SIGINT', () => process.emit('SIGINT'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup interrupt handlers so we can cancel the build if the user presses
|
||||||
|
// ctrl+c
|
||||||
|
|
||||||
|
// This is necessary because the `exit-hook` module is used by several
|
||||||
|
// dependencies, and will exit without calling the following handler.
|
||||||
|
// Once https://github.com/resin-io/resin-cli/issues/867 has been solved,
|
||||||
|
// we are free to (and definitely should) remove the below line
|
||||||
|
process.removeAllListeners('SIGINT');
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
process.stderr.write('Received SIGINT, cleaning up. Please wait.\n');
|
||||||
|
cancelBuildIfNecessary(build).then(() => {
|
||||||
|
stream.end();
|
||||||
|
process.exit(130);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('data', getBuilderMessageHandler(build));
|
||||||
|
stream.on('end', resolve);
|
||||||
|
stream.on('error', reject);
|
||||||
|
}).return();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleBuilderMetadata(obj: BuilderMessage, build: RemoteBuild) {
|
||||||
|
const { stripIndent } = await import('common-tags');
|
||||||
|
|
||||||
|
switch (obj.resource) {
|
||||||
|
case 'cursor':
|
||||||
|
const readline = await import('readline');
|
||||||
|
|
||||||
|
if (obj.value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = obj.value.match(CURSOR_METADATA_REGEX);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
// FIXME: Make this error nicer.
|
||||||
|
console.log(
|
||||||
|
stripIndent`
|
||||||
|
Warning: ignoring unknown builder command. You may experience
|
||||||
|
odd build output. Maybe you need to update resin-cli?`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = match[1];
|
||||||
|
const amount = match[2] || 1;
|
||||||
|
|
||||||
|
switch (value) {
|
||||||
|
case 'erase':
|
||||||
|
readline.clearLine(process.stdout, 0);
|
||||||
|
process.stdout.write('\r');
|
||||||
|
break;
|
||||||
|
case 'up':
|
||||||
|
readline.moveCursor(process.stdout, 0, -amount);
|
||||||
|
break;
|
||||||
|
case 'down':
|
||||||
|
readline.moveCursor(process.stdout, 0, amount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'buildLogId':
|
||||||
|
// The name of this resource is slightly dated, but this is the release
|
||||||
|
// id from the API. We need to save this so that if the user ctrl+c's the
|
||||||
|
// build we can cancel it on the API.
|
||||||
|
build.releaseId = parseInt(obj.value!, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBuilderMessageHandler(
|
||||||
|
build: RemoteBuild,
|
||||||
|
): (obj: BuilderMessage) => Promise<void> {
|
||||||
|
return async (obj: BuilderMessage) => {
|
||||||
|
if (DEBUG_MODE) {
|
||||||
|
console.log(`[debug] handling message: ${JSON.stringify(obj)}`);
|
||||||
|
}
|
||||||
|
if (obj.type != null && obj.type === 'metadata') {
|
||||||
|
return handleBuilderMetadata(obj, build);
|
||||||
|
}
|
||||||
|
if (obj.message) {
|
||||||
|
const readline = await import('readline');
|
||||||
|
readline.clearLine(process.stdout, 0);
|
||||||
|
|
||||||
|
const message = obj.message.replace(TRIM_REGEX, '');
|
||||||
|
if (obj.replace) {
|
||||||
|
process.stdout.write(`\r${message}`);
|
||||||
|
} else {
|
||||||
|
process.stdout.write(`\r${message}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cancelBuildIfNecessary(build: RemoteBuild): Promise<void> {
|
||||||
|
if (build.releaseId != null) {
|
||||||
|
await build.sdk.pine.patch({
|
||||||
|
resource: 'release',
|
||||||
|
id: build.releaseId,
|
||||||
|
body: {
|
||||||
|
status: 'cancelled',
|
||||||
|
end_timestamp: Date.now(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getRequestStream(build: RemoteBuild): Promise<Stream.Duplex> {
|
||||||
|
const path = await import('path');
|
||||||
|
const visuals = await import('resin-cli-visuals');
|
||||||
|
|
||||||
|
const tarSpinner = new visuals.Spinner('Packaging the project source...');
|
||||||
|
tarSpinner.start();
|
||||||
|
// Tar the directory so that we can send it to the builder
|
||||||
|
const tarStream = await tarDirectory(path.resolve(build.source));
|
||||||
|
tarSpinner.stop();
|
||||||
|
|
||||||
|
if (DEBUG_MODE) {
|
||||||
|
console.log('[debug] Opening builder connection');
|
||||||
|
}
|
||||||
|
const post = request.post({
|
||||||
|
url: await getBuilderEndpoint(build.baseUrl, build.owner, build.app),
|
||||||
|
auth: {
|
||||||
|
bearer: build.auth,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploadSpinner = new visuals.Spinner(
|
||||||
|
'Uploading source package to resin cloud',
|
||||||
|
);
|
||||||
|
uploadSpinner.start();
|
||||||
|
|
||||||
|
tarStream.pipe(post);
|
||||||
|
|
||||||
|
const parseStream = post.pipe(JSONStream.parse('*'));
|
||||||
|
parseStream.on('data', () => uploadSpinner.stop());
|
||||||
|
return parseStream as Stream.Duplex;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "resin-cli",
|
"name": "resin-cli",
|
||||||
"version": "7.3.8",
|
"version": "7.4.0",
|
||||||
"description": "The official resin.io CLI tool",
|
"description": "The official resin.io CLI tool",
|
||||||
"main": "./build/actions/index.js",
|
"main": "./build/actions/index.js",
|
||||||
"homepage": "https://github.com/resin-io/resin-cli",
|
"homepage": "https://github.com/resin-io/resin-cli",
|
||||||
@ -89,6 +89,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@resin.io/valid-email": "^0.1.0",
|
"@resin.io/valid-email": "^0.1.0",
|
||||||
|
"@types/stream-to-promise": "^2.2.0",
|
||||||
"ansi-escapes": "^2.0.0",
|
"ansi-escapes": "^2.0.0",
|
||||||
"any-promise": "^1.3.0",
|
"any-promise": "^1.3.0",
|
||||||
"archiver": "^2.1.0",
|
"archiver": "^2.1.0",
|
||||||
@ -117,6 +118,7 @@
|
|||||||
"inquirer": "^3.1.1",
|
"inquirer": "^3.1.1",
|
||||||
"is-root": "^1.0.0",
|
"is-root": "^1.0.0",
|
||||||
"js-yaml": "^3.10.0",
|
"js-yaml": "^3.10.0",
|
||||||
|
"JSONStream": "^1.0.3",
|
||||||
"klaw": "^1.3.1",
|
"klaw": "^1.3.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"mixpanel": "^0.4.0",
|
"mixpanel": "^0.4.0",
|
||||||
@ -146,7 +148,7 @@
|
|||||||
"resin-multibuild": "^0.5.1",
|
"resin-multibuild": "^0.5.1",
|
||||||
"resin-preload": "^6.2.0",
|
"resin-preload": "^6.2.0",
|
||||||
"resin-release": "^1.2.0",
|
"resin-release": "^1.2.0",
|
||||||
"resin-sdk": "9.0.0-beta17",
|
"resin-sdk": "9.0.0-beta18",
|
||||||
"resin-sdk-preconfigured": "^6.9.0",
|
"resin-sdk-preconfigured": "^6.9.0",
|
||||||
"resin-settings-client": "^3.6.1",
|
"resin-settings-client": "^3.6.1",
|
||||||
"resin-stream-logger": "^0.1.0",
|
"resin-stream-logger": "^0.1.0",
|
||||||
|
53
typings/JSONStream.d.ts
vendored
Normal file
53
typings/JSONStream.d.ts
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// These are the DefinitelyTyped typings for JSONStream, but because of this
|
||||||
|
// mismatch in case of jsonstream and JSONStream, it is necessary to include
|
||||||
|
// them this way, with an upper case module declaration. They have also
|
||||||
|
// been slightly edited to remove the extra `declare` keyworks (which are
|
||||||
|
// not necessary or accepted inside a `declare module '...' {` block)
|
||||||
|
declare module 'JSONStream' {
|
||||||
|
// Type definitions for JSONStream v0.8.0
|
||||||
|
// Project: https://github.com/dominictarr/JSONStream
|
||||||
|
// Definitions by: Bart van der Schoor <https://github.com/Bartvds>
|
||||||
|
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||||
|
|
||||||
|
/// <reference types="node" />
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
recurse: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse(pattern: any): NodeJS.ReadWriteStream;
|
||||||
|
export function parse(patterns: any[]): NodeJS.ReadWriteStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a writable stream.
|
||||||
|
* you may pass in custom open, close, and seperator strings. But, by default,
|
||||||
|
* JSONStream.stringify() will create an array,
|
||||||
|
* (with default options open='[\n', sep='\n,\n', close='\n]\n')
|
||||||
|
*/
|
||||||
|
export function stringify(): NodeJS.ReadWriteStream;
|
||||||
|
|
||||||
|
/** If you call JSONStream.stringify(false) the elements will only be seperated by a newline. */
|
||||||
|
export function stringify(
|
||||||
|
newlineOnly: NewlineOnlyIndicator,
|
||||||
|
): NodeJS.ReadWriteStream;
|
||||||
|
type NewlineOnlyIndicator = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a writable stream.
|
||||||
|
* you may pass in custom open, close, and seperator strings. But, by default,
|
||||||
|
* JSONStream.stringify() will create an array,
|
||||||
|
* (with default options open='[\n', sep='\n,\n', close='\n]\n')
|
||||||
|
*/
|
||||||
|
export function stringify(
|
||||||
|
open: string,
|
||||||
|
sep: string,
|
||||||
|
close: string,
|
||||||
|
): NodeJS.ReadWriteStream;
|
||||||
|
|
||||||
|
export function stringifyObject(): NodeJS.ReadWriteStream;
|
||||||
|
export function stringifyObject(
|
||||||
|
open: string,
|
||||||
|
sep: string,
|
||||||
|
close: string,
|
||||||
|
): NodeJS.ReadWriteStream;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user