node_modules ignore

This commit is contained in:
oier committed 2025-05-08 23:43:47 +02:00
1 parent e19d52f172
commit 4574544c9f
65041 files changed
+10593536

No files matched your search

@@ -0,0 +1,47 @@
/// <reference types="node" />
import { Writable } from 'stream';
import { WebSocket } from 'ws';
import type { Struct, Utils } from '@strapi/types';
import { createDispatcher } from '../utils';
import type { IDestinationProvider, IMetadata, ProviderType, TransferStage } from '../../../../types';
import type { IDiagnosticReporter } from '../../../utils/diagnostic';
import type { Auth } from '../../../../types/remote/protocol';
import type { ILocalStrapiDestinationProviderOptions } from '../local-destination';
export interface IRemoteStrapiDestinationProviderOptions extends Pick<ILocalStrapiDestinationProviderOptions, 'restore' | 'strategy'> {
url: URL;
auth?: Auth.ITransferTokenAuth;
retryMessageOptions?: {
retryMessageTimeout: number;
retryMessageMaxRetries: number;
};
}
declare class RemoteStrapiDestinationProvider implements IDestinationProvider {
#private;
name: string;
type: ProviderType;
options: IRemoteStrapiDestinationProviderOptions;
ws: WebSocket | null;
dispatcher: ReturnType<typeof createDispatcher> | null;
transferID: string | null;
stats: {
[TStage in Exclude<TransferStage, 'schemas'>]: {
count: number;
};
};
constructor(options: IRemoteStrapiDestinationProviderOptions);
private resetStats;
initTransfer(): Promise<string>;
bootstrap(diagnostics?: IDiagnosticReporter): Promise<void>;
close(): Promise<void>;
getMetadata(): Promise<IMetadata | null> | null;
beforeTransfer(): Promise<void>;
rollback(): Promise<void>;
getSchemas(): Promise<Utils.String.Dict<Struct.Schema> | null>;
createEntitiesWriteStream(): Writable;
createLinksWriteStream(): Writable;
createConfigurationWriteStream(): Writable;
createAssetsWriteStream(): Writable | Promise<Writable>;
}
export declare const createRemoteStrapiDestinationProvider: (options: IRemoteStrapiDestinationProviderOptions) => RemoteStrapiDestinationProvider;
export {};
//# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/strapi/providers/remote-destination/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAE/B,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAyC,MAAM,UAAU,CAAC;AAEnF,OAAO,KAAK,EACV,oBAAoB,EACpB,SAAS,EACT,YAAY,EAEZ,aAAa,EAEd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAkB,IAAI,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,sCAAsC,EAAE,MAAM,sBAAsB,CAAC;AAInF,MAAM,WAAW,uCACf,SAAQ,IAAI,CAAC,sCAAsC,EAAE,SAAS,GAAG,UAAU,CAAC;IAC5E,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC;IAC/B,mBAAmB,CAAC,EAAE;QACpB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,sBAAsB,EAAE,MAAM,CAAC;KAChC,CAAC;CACH;AAID,cAAM,+BAAgC,YAAW,oBAAoB;;IACnE,IAAI,SAAgC;IAEpC,IAAI,EAAE,YAAY,CAAiB;IAEnC,OAAO,EAAE,uCAAuC,CAAC;IAEjD,EAAE,EAAE,SAAS,GAAG,IAAI,CAAC;IAErB,UAAU,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAAG,IAAI,CAAC;IAEvD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B,KAAK,EAAG;SAAG,MAAM,IAAI,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE;KAAE,CAAC;gBAIjE,OAAO,EAAE,uCAAuC;IAS5D,OAAO,CAAC,UAAU;IASZ,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAsK/B,SAAS,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+D3D,KAAK;IAuBX,WAAW;IAIL,cAAc;IAId,QAAQ;IAId,UAAU;IAQV,yBAAyB,IAAI,QAAQ;IAIrC,sBAAsB,IAAI,QAAQ;IAIlC,8BAA8B,IAAI,QAAQ;IAI1C,uBAAuB,IAAI,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;CAiFxD;AAED,eAAO,MAAM,qCAAqC,YACvC,uCAAuC,oCAGjD,CAAC"}
@@ -0,0 +1,392 @@
'use strict';
var crypto = require('crypto');
var stream = require('stream');
var fp = require('lodash/fp');
var utils = require('../utils.js');
var constants = require('../../remote/constants.js');
var providers = require('../../../errors/providers.js');
function _class_private_field_loose_base(receiver, privateKey) {
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
throw new TypeError("attempted to use private field on non-instance");
}
return receiver;
}
var id = 0;
function _class_private_field_loose_key(name) {
return "__private_" + id++ + "_" + name;
}
const jsonLength = (obj)=>Buffer.byteLength(JSON.stringify(obj));
var _diagnostics = /*#__PURE__*/ _class_private_field_loose_key("_diagnostics"), _startStepOnce = /*#__PURE__*/ _class_private_field_loose_key("_startStepOnce"), _startStep = /*#__PURE__*/ _class_private_field_loose_key("_startStep"), _endStep = /*#__PURE__*/ _class_private_field_loose_key("_endStep"), _streamStep = /*#__PURE__*/ _class_private_field_loose_key("_streamStep"), _writeStream = /*#__PURE__*/ _class_private_field_loose_key("_writeStream"), _reportInfo = /*#__PURE__*/ _class_private_field_loose_key("_reportInfo");
class RemoteStrapiDestinationProvider {
resetStats() {
this.stats = {
assets: {
count: 0
},
entities: {
count: 0
},
links: {
count: 0
},
configuration: {
count: 0
}
};
}
async initTransfer() {
const { strategy, restore } = this.options;
const query = this.dispatcher?.dispatchCommand({
command: 'init',
params: {
options: {
strategy,
restore
},
transfer: 'push'
}
});
const res = await query;
if (!res?.transferID) {
throw new providers.ProviderTransferError('Init failed, invalid response from the server');
}
this.resetStats();
return res.transferID;
}
async bootstrap(diagnostics) {
_class_private_field_loose_base(this, _diagnostics)[_diagnostics] = diagnostics;
const { url, auth } = this.options;
const validProtocols = [
'https:',
'http:'
];
let ws;
if (!validProtocols.includes(url.protocol)) {
throw new providers.ProviderValidationError(`Invalid protocol "${url.protocol}"`, {
check: 'url',
details: {
protocol: url.protocol,
validProtocols
}
});
}
const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${wsProtocol}//${url.host}${utils.trimTrailingSlash(url.pathname)}${constants.TRANSFER_PATH}/push`;
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('establishing websocket connection');
// No auth defined, trying public access for transfer
if (!auth) {
ws = await utils.connectToWebsocket(wsUrl, undefined, _class_private_field_loose_base(this, _diagnostics)[_diagnostics]);
} else if (auth.type === 'token') {
const headers = {
Authorization: `Bearer ${auth.token}`
};
ws = await utils.connectToWebsocket(wsUrl, {
headers
}, _class_private_field_loose_base(this, _diagnostics)[_diagnostics]);
} else {
throw new providers.ProviderValidationError('Auth method not available', {
check: 'auth.type',
details: {
auth: auth.type
}
});
}
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('established websocket connection');
this.ws = ws;
const { retryMessageOptions } = this.options;
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('creating dispatcher');
this.dispatcher = utils.createDispatcher(this.ws, retryMessageOptions, (message)=>_class_private_field_loose_base(this, _reportInfo)[_reportInfo](message));
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('created dispatcher');
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('initialize transfer');
this.transferID = await this.initTransfer();
_class_private_field_loose_base(this, _reportInfo)[_reportInfo](`initialized transfer ${this.transferID}`);
this.dispatcher.setTransferProperties({
id: this.transferID,
kind: 'push'
});
await this.dispatcher.dispatchTransferAction('bootstrap');
}
async close() {
// Gracefully close the remote transfer process
if (this.transferID && this.dispatcher) {
await this.dispatcher.dispatchTransferAction('close');
await this.dispatcher.dispatchCommand({
command: 'end',
params: {
transferID: this.transferID
}
});
}
await new Promise((resolve)=>{
const { ws } = this;
if (!ws || ws.CLOSED) {
resolve();
return;
}
ws.on('close', ()=>resolve()).close();
});
}
getMetadata() {
return this.dispatcher?.dispatchTransferAction('getMetadata') ?? null;
}
async beforeTransfer() {
await this.dispatcher?.dispatchTransferAction('beforeTransfer');
}
async rollback() {
await this.dispatcher?.dispatchTransferAction('rollback');
}
getSchemas() {
if (!this.dispatcher) {
return Promise.resolve(null);
}
return this.dispatcher.dispatchTransferAction('getSchemas');
}
createEntitiesWriteStream() {
return _class_private_field_loose_base(this, _writeStream)[_writeStream]('entities');
}
createLinksWriteStream() {
return _class_private_field_loose_base(this, _writeStream)[_writeStream]('links');
}
createConfigurationWriteStream() {
return _class_private_field_loose_base(this, _writeStream)[_writeStream]('configuration');
}
createAssetsWriteStream() {
let batch = [];
let hasStarted = false;
const batchSize = 1024 * 1024; // 1MB;
const batchLength = ()=>{
return batch.reduce((acc, chunk)=>chunk.action === 'stream' ? acc + chunk.data.byteLength : acc, 0);
};
const startAssetsTransferOnce = _class_private_field_loose_base(this, _startStepOnce)[_startStepOnce]('assets');
const flush = async ()=>{
const streamError = await _class_private_field_loose_base(this, _streamStep)[_streamStep]('assets', batch);
batch = [];
return streamError;
};
const safePush = async (chunk)=>{
batch.push(chunk);
if (batchLength() >= batchSize) {
const streamError = await flush();
if (streamError) {
throw streamError;
}
}
};
return new stream.Writable({
objectMode: true,
final: async (callback)=>{
if (batch.length > 0) {
await flush();
}
if (hasStarted) {
const { error: endStepError } = await _class_private_field_loose_base(this, _endStep)[_endStep]('assets');
if (endStepError) {
return callback(endStepError);
}
}
return callback(null);
},
async write (asset, _encoding, callback) {
const startError = await startAssetsTransferOnce();
if (startError) {
return callback(startError);
}
hasStarted = true;
const assetID = crypto.randomUUID();
const { filename, filepath, stats, stream, metadata } = asset;
try {
await safePush({
action: 'start',
assetID,
data: {
filename,
filepath,
stats,
metadata
}
});
for await (const chunk of stream){
await safePush({
action: 'stream',
assetID,
data: chunk
});
}
await safePush({
action: 'end',
assetID
});
callback();
} catch (error) {
if (error instanceof Error) {
callback(error);
}
}
}
});
}
constructor(options){
Object.defineProperty(this, _startStepOnce, {
value: startStepOnce
});
Object.defineProperty(this, _startStep, {
value: startStep
});
Object.defineProperty(this, _endStep, {
value: endStep
});
Object.defineProperty(this, _streamStep, {
value: streamStep
});
Object.defineProperty(this, _writeStream, {
value: writeStream
});
Object.defineProperty(this, _reportInfo, {
value: reportInfo
});
Object.defineProperty(this, _diagnostics, {
writable: true,
value: void 0
});
this.name = 'destination::remote-strapi';
this.type = 'destination';
this.options = options;
this.ws = null;
this.dispatcher = null;
this.transferID = null;
this.resetStats();
}
}
function startStepOnce(stage) {
return fp.once(()=>_class_private_field_loose_base(this, _startStep)[_startStep](stage));
}
async function startStep(step) {
try {
await this.dispatcher?.dispatchTransferStep({
action: 'start',
step
});
} catch (e) {
if (e instanceof Error) {
return e;
}
if (typeof e === 'string') {
return new providers.ProviderTransferError(e);
}
return new providers.ProviderTransferError('Unexpected error');
}
this.stats[step] = {
count: 0
};
return null;
}
async function endStep(step) {
try {
const res = await this.dispatcher?.dispatchTransferStep({
action: 'end',
step
});
return {
stats: res?.stats ?? null,
error: null
};
} catch (e) {
if (e instanceof Error) {
return {
stats: null,
error: e
};
}
if (typeof e === 'string') {
return {
stats: null,
error: new providers.ProviderTransferError(e)
};
}
return {
stats: null,
error: new providers.ProviderTransferError('Unexpected error')
};
}
}
async function streamStep(step, message) {
try {
if (step === 'assets') {
const assetMessage = message;
this.stats[step].count += assetMessage.filter((data)=>data.action === 'start').length;
} else {
this.stats[step].count += message.length;
}
await this.dispatcher?.dispatchTransferStep({
action: 'stream',
step,
data: message
});
} catch (e) {
if (e instanceof Error) {
return e;
}
if (typeof e === 'string') {
return new providers.ProviderTransferError(e);
}
return new providers.ProviderTransferError('Unexpected error');
}
return null;
}
function writeStream(step) {
const batchSize = 1024 * 1024; // 1MB;
const startTransferOnce = _class_private_field_loose_base(this, _startStepOnce)[_startStepOnce](step);
let batch = [];
const batchLength = ()=>jsonLength(batch);
return new stream.Writable({
objectMode: true,
final: async (callback)=>{
if (batch.length > 0) {
const streamError = await _class_private_field_loose_base(this, _streamStep)[_streamStep](step, batch);
batch = [];
if (streamError) {
return callback(streamError);
}
}
const { error, stats } = await _class_private_field_loose_base(this, _endStep)[_endStep](step);
const { count } = this.stats[step];
if (stats && (stats.started !== count || stats.finished !== count)) {
callback(new Error(`Data missing: sent ${this.stats[step].count} ${step}, recieved ${stats.started} and saved ${stats.finished} ${step}`));
}
callback(error);
},
write: async (chunk, _encoding, callback)=>{
const startError = await startTransferOnce();
if (startError) {
return callback(startError);
}
batch.push(chunk);
if (batchLength() >= batchSize) {
const streamError = await _class_private_field_loose_base(this, _streamStep)[_streamStep](step, batch);
batch = [];
if (streamError) {
return callback(streamError);
}
}
callback();
}
});
}
function reportInfo(message) {
_class_private_field_loose_base(this, _diagnostics)[_diagnostics]?.report({
details: {
createdAt: new Date(),
message,
origin: 'remote-destination-provider'
},
kind: 'info'
});
}
const createRemoteStrapiDestinationProvider = (options)=>{
return new RemoteStrapiDestinationProvider(options);
};
exports.createRemoteStrapiDestinationProvider = createRemoteStrapiDestinationProvider;
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sources":["../../../../src/strapi/providers/remote-destination/index.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport { Writable } from 'stream';\nimport { WebSocket } from 'ws';\nimport { once } from 'lodash/fp';\nimport type { Struct, Utils } from '@strapi/types';\n\nimport { createDispatcher, connectToWebsocket, trimTrailingSlash } from '../utils';\n\nimport type {\n IDestinationProvider,\n IMetadata,\n ProviderType,\n IAsset,\n TransferStage,\n Protocol,\n} from '../../../../types';\nimport type { IDiagnosticReporter } from '../../../utils/diagnostic';\nimport type { Client, Server, Auth } from '../../../../types/remote/protocol';\nimport type { ILocalStrapiDestinationProviderOptions } from '../local-destination';\nimport { TRANSFER_PATH } from '../../remote/constants';\nimport { ProviderTransferError, ProviderValidationError } from '../../../errors/providers';\n\nexport interface IRemoteStrapiDestinationProviderOptions\n extends Pick<ILocalStrapiDestinationProviderOptions, 'restore' | 'strategy'> {\n url: URL; // the url of the remote Strapi admin\n auth?: Auth.ITransferTokenAuth;\n retryMessageOptions?: {\n retryMessageTimeout: number; // milliseconds to wait for a response from a message\n retryMessageMaxRetries: number; // max number of retries for a message before aborting transfer\n };\n}\n\nconst jsonLength = (obj: object) => Buffer.byteLength(JSON.stringify(obj));\n\nclass RemoteStrapiDestinationProvider implements IDestinationProvider {\n name = 'destination::remote-strapi';\n\n type: ProviderType = 'destination';\n\n options: IRemoteStrapiDestinationProviderOptions;\n\n ws: WebSocket | null;\n\n dispatcher: ReturnType<typeof createDispatcher> | null;\n\n transferID: string | null;\n\n stats!: { [TStage in Exclude<TransferStage, 'schemas'>]: { count: number } };\n\n #diagnostics?: IDiagnosticReporter;\n\n constructor(options: IRemoteStrapiDestinationProviderOptions) {\n this.options = options;\n this.ws = null;\n this.dispatcher = null;\n this.transferID = null;\n\n this.resetStats();\n }\n\n private resetStats() {\n this.stats = {\n assets: { count: 0 },\n entities: { count: 0 },\n links: { count: 0 },\n configuration: { count: 0 },\n };\n }\n\n async initTransfer(): Promise<string> {\n const { strategy, restore } = this.options;\n\n const query = this.dispatcher?.dispatchCommand({\n command: 'init',\n params: { options: { strategy, restore }, transfer: 'push' },\n });\n\n const res = (await query) as Server.Payload<Server.InitMessage>;\n if (!res?.transferID) {\n throw new ProviderTransferError('Init failed, invalid response from the server');\n }\n\n this.resetStats();\n\n return res.transferID;\n }\n\n #startStepOnce(stage: Client.TransferPushStep) {\n return once(() => this.#startStep(stage));\n }\n\n async #startStep<T extends Client.TransferPushStep>(step: T) {\n try {\n await this.dispatcher?.dispatchTransferStep({ action: 'start', step });\n } catch (e) {\n if (e instanceof Error) {\n return e;\n }\n\n if (typeof e === 'string') {\n return new ProviderTransferError(e);\n }\n\n return new ProviderTransferError('Unexpected error');\n }\n\n this.stats[step] = { count: 0 };\n\n return null;\n }\n\n async #endStep<T extends Client.TransferPushStep>(step: T) {\n try {\n const res = await this.dispatcher?.dispatchTransferStep<{\n ok: boolean;\n stats: Protocol.Client.Stats;\n }>({\n action: 'end',\n step,\n });\n\n return { stats: res?.stats ?? null, error: null };\n } catch (e) {\n if (e instanceof Error) {\n return { stats: null, error: e };\n }\n\n if (typeof e === 'string') {\n return { stats: null, error: new ProviderTransferError(e) };\n }\n\n return { stats: null, error: new ProviderTransferError('Unexpected error') };\n }\n }\n\n async #streamStep<T extends Client.TransferPushStep>(\n step: T,\n message: Client.GetTransferPushStreamData<T>\n ) {\n try {\n if (step === 'assets') {\n const assetMessage = message as Protocol.Client.TransferAssetFlow[];\n this.stats[step].count += assetMessage.filter((data) => data.action === 'start').length;\n } else {\n this.stats[step].count += message.length;\n }\n\n await this.dispatcher?.dispatchTransferStep({ action: 'stream', step, data: message });\n } catch (e) {\n if (e instanceof Error) {\n return e;\n }\n\n if (typeof e === 'string') {\n return new ProviderTransferError(e);\n }\n\n return new ProviderTransferError('Unexpected error');\n }\n\n return null;\n }\n\n #writeStream(step: Exclude<Client.TransferPushStep, 'assets'>): Writable {\n type Step = typeof step;\n\n const batchSize = 1024 * 1024; // 1MB;\n consLine truncated
@@ -0,0 +1,390 @@
import { randomUUID } from 'crypto';
import { Writable } from 'stream';
import { once } from 'lodash/fp';
import { trimTrailingSlash, connectToWebsocket, createDispatcher } from '../utils.mjs';
import { TRANSFER_PATH } from '../../remote/constants.mjs';
import { ProviderTransferError, ProviderValidationError } from '../../../errors/providers.mjs';
function _class_private_field_loose_base(receiver, privateKey) {
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
throw new TypeError("attempted to use private field on non-instance");
}
return receiver;
}
var id = 0;
function _class_private_field_loose_key(name) {
return "__private_" + id++ + "_" + name;
}
const jsonLength = (obj)=>Buffer.byteLength(JSON.stringify(obj));
var _diagnostics = /*#__PURE__*/ _class_private_field_loose_key("_diagnostics"), _startStepOnce = /*#__PURE__*/ _class_private_field_loose_key("_startStepOnce"), _startStep = /*#__PURE__*/ _class_private_field_loose_key("_startStep"), _endStep = /*#__PURE__*/ _class_private_field_loose_key("_endStep"), _streamStep = /*#__PURE__*/ _class_private_field_loose_key("_streamStep"), _writeStream = /*#__PURE__*/ _class_private_field_loose_key("_writeStream"), _reportInfo = /*#__PURE__*/ _class_private_field_loose_key("_reportInfo");
class RemoteStrapiDestinationProvider {
resetStats() {
this.stats = {
assets: {
count: 0
},
entities: {
count: 0
},
links: {
count: 0
},
configuration: {
count: 0
}
};
}
async initTransfer() {
const { strategy, restore } = this.options;
const query = this.dispatcher?.dispatchCommand({
command: 'init',
params: {
options: {
strategy,
restore
},
transfer: 'push'
}
});
const res = await query;
if (!res?.transferID) {
throw new ProviderTransferError('Init failed, invalid response from the server');
}
this.resetStats();
return res.transferID;
}
async bootstrap(diagnostics) {
_class_private_field_loose_base(this, _diagnostics)[_diagnostics] = diagnostics;
const { url, auth } = this.options;
const validProtocols = [
'https:',
'http:'
];
let ws;
if (!validProtocols.includes(url.protocol)) {
throw new ProviderValidationError(`Invalid protocol "${url.protocol}"`, {
check: 'url',
details: {
protocol: url.protocol,
validProtocols
}
});
}
const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${wsProtocol}//${url.host}${trimTrailingSlash(url.pathname)}${TRANSFER_PATH}/push`;
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('establishing websocket connection');
// No auth defined, trying public access for transfer
if (!auth) {
ws = await connectToWebsocket(wsUrl, undefined, _class_private_field_loose_base(this, _diagnostics)[_diagnostics]);
} else if (auth.type === 'token') {
const headers = {
Authorization: `Bearer ${auth.token}`
};
ws = await connectToWebsocket(wsUrl, {
headers
}, _class_private_field_loose_base(this, _diagnostics)[_diagnostics]);
} else {
throw new ProviderValidationError('Auth method not available', {
check: 'auth.type',
details: {
auth: auth.type
}
});
}
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('established websocket connection');
this.ws = ws;
const { retryMessageOptions } = this.options;
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('creating dispatcher');
this.dispatcher = createDispatcher(this.ws, retryMessageOptions, (message)=>_class_private_field_loose_base(this, _reportInfo)[_reportInfo](message));
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('created dispatcher');
_class_private_field_loose_base(this, _reportInfo)[_reportInfo]('initialize transfer');
this.transferID = await this.initTransfer();
_class_private_field_loose_base(this, _reportInfo)[_reportInfo](`initialized transfer ${this.transferID}`);
this.dispatcher.setTransferProperties({
id: this.transferID,
kind: 'push'
});
await this.dispatcher.dispatchTransferAction('bootstrap');
}
async close() {
// Gracefully close the remote transfer process
if (this.transferID && this.dispatcher) {
await this.dispatcher.dispatchTransferAction('close');
await this.dispatcher.dispatchCommand({
command: 'end',
params: {
transferID: this.transferID
}
});
}
await new Promise((resolve)=>{
const { ws } = this;
if (!ws || ws.CLOSED) {
resolve();
return;
}
ws.on('close', ()=>resolve()).close();
});
}
getMetadata() {
return this.dispatcher?.dispatchTransferAction('getMetadata') ?? null;
}
async beforeTransfer() {
await this.dispatcher?.dispatchTransferAction('beforeTransfer');
}
async rollback() {
await this.dispatcher?.dispatchTransferAction('rollback');
}
getSchemas() {
if (!this.dispatcher) {
return Promise.resolve(null);
}
return this.dispatcher.dispatchTransferAction('getSchemas');
}
createEntitiesWriteStream() {
return _class_private_field_loose_base(this, _writeStream)[_writeStream]('entities');
}
createLinksWriteStream() {
return _class_private_field_loose_base(this, _writeStream)[_writeStream]('links');
}
createConfigurationWriteStream() {
return _class_private_field_loose_base(this, _writeStream)[_writeStream]('configuration');
}
createAssetsWriteStream() {
let batch = [];
let hasStarted = false;
const batchSize = 1024 * 1024; // 1MB;
const batchLength = ()=>{
return batch.reduce((acc, chunk)=>chunk.action === 'stream' ? acc + chunk.data.byteLength : acc, 0);
};
const startAssetsTransferOnce = _class_private_field_loose_base(this, _startStepOnce)[_startStepOnce]('assets');
const flush = async ()=>{
const streamError = await _class_private_field_loose_base(this, _streamStep)[_streamStep]('assets', batch);
batch = [];
return streamError;
};
const safePush = async (chunk)=>{
batch.push(chunk);
if (batchLength() >= batchSize) {
const streamError = await flush();
if (streamError) {
throw streamError;
}
}
};
return new Writable({
objectMode: true,
final: async (callback)=>{
if (batch.length > 0) {
await flush();
}
if (hasStarted) {
const { error: endStepError } = await _class_private_field_loose_base(this, _endStep)[_endStep]('assets');
if (endStepError) {
return callback(endStepError);
}
}
return callback(null);
},
async write (asset, _encoding, callback) {
const startError = await startAssetsTransferOnce();
if (startError) {
return callback(startError);
}
hasStarted = true;
const assetID = randomUUID();
const { filename, filepath, stats, stream, metadata } = asset;
try {
await safePush({
action: 'start',
assetID,
data: {
filename,
filepath,
stats,
metadata
}
});
for await (const chunk of stream){
await safePush({
action: 'stream',
assetID,
data: chunk
});
}
await safePush({
action: 'end',
assetID
});
callback();
} catch (error) {
if (error instanceof Error) {
callback(error);
}
}
}
});
}
constructor(options){
Object.defineProperty(this, _startStepOnce, {
value: startStepOnce
});
Object.defineProperty(this, _startStep, {
value: startStep
});
Object.defineProperty(this, _endStep, {
value: endStep
});
Object.defineProperty(this, _streamStep, {
value: streamStep
});
Object.defineProperty(this, _writeStream, {
value: writeStream
});
Object.defineProperty(this, _reportInfo, {
value: reportInfo
});
Object.defineProperty(this, _diagnostics, {
writable: true,
value: void 0
});
this.name = 'destination::remote-strapi';
this.type = 'destination';
this.options = options;
this.ws = null;
this.dispatcher = null;
this.transferID = null;
this.resetStats();
}
}
function startStepOnce(stage) {
return once(()=>_class_private_field_loose_base(this, _startStep)[_startStep](stage));
}
async function startStep(step) {
try {
await this.dispatcher?.dispatchTransferStep({
action: 'start',
step
});
} catch (e) {
if (e instanceof Error) {
return e;
}
if (typeof e === 'string') {
return new ProviderTransferError(e);
}
return new ProviderTransferError('Unexpected error');
}
this.stats[step] = {
count: 0
};
return null;
}
async function endStep(step) {
try {
const res = await this.dispatcher?.dispatchTransferStep({
action: 'end',
step
});
return {
stats: res?.stats ?? null,
error: null
};
} catch (e) {
if (e instanceof Error) {
return {
stats: null,
error: e
};
}
if (typeof e === 'string') {
return {
stats: null,
error: new ProviderTransferError(e)
};
}
return {
stats: null,
error: new ProviderTransferError('Unexpected error')
};
}
}
async function streamStep(step, message) {
try {
if (step === 'assets') {
const assetMessage = message;
this.stats[step].count += assetMessage.filter((data)=>data.action === 'start').length;
} else {
this.stats[step].count += message.length;
}
await this.dispatcher?.dispatchTransferStep({
action: 'stream',
step,
data: message
});
} catch (e) {
if (e instanceof Error) {
return e;
}
if (typeof e === 'string') {
return new ProviderTransferError(e);
}
return new ProviderTransferError('Unexpected error');
}
return null;
}
function writeStream(step) {
const batchSize = 1024 * 1024; // 1MB;
const startTransferOnce = _class_private_field_loose_base(this, _startStepOnce)[_startStepOnce](step);
let batch = [];
const batchLength = ()=>jsonLength(batch);
return new Writable({
objectMode: true,
final: async (callback)=>{
if (batch.length > 0) {
const streamError = await _class_private_field_loose_base(this, _streamStep)[_streamStep](step, batch);
batch = [];
if (streamError) {
return callback(streamError);
}
}
const { error, stats } = await _class_private_field_loose_base(this, _endStep)[_endStep](step);
const { count } = this.stats[step];
if (stats && (stats.started !== count || stats.finished !== count)) {
callback(new Error(`Data missing: sent ${this.stats[step].count} ${step}, recieved ${stats.started} and saved ${stats.finished} ${step}`));
}
callback(error);
},
write: async (chunk, _encoding, callback)=>{
const startError = await startTransferOnce();
if (startError) {
return callback(startError);
}
batch.push(chunk);
if (batchLength() >= batchSize) {
const streamError = await _class_private_field_loose_base(this, _streamStep)[_streamStep](step, batch);
batch = [];
if (streamError) {
return callback(streamError);
}
}
callback();
}
});
}
function reportInfo(message) {
_class_private_field_loose_base(this, _diagnostics)[_diagnostics]?.report({
details: {
createdAt: new Date(),
message,
origin: 'remote-destination-provider'
},
kind: 'info'
});
}
const createRemoteStrapiDestinationProvider = (options)=>{
return new RemoteStrapiDestinationProvider(options);
};
export { createRemoteStrapiDestinationProvider };
//# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"index.mjs","sources":["../../../../src/strapi/providers/remote-destination/index.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport { Writable } from 'stream';\nimport { WebSocket } from 'ws';\nimport { once } from 'lodash/fp';\nimport type { Struct, Utils } from '@strapi/types';\n\nimport { createDispatcher, connectToWebsocket, trimTrailingSlash } from '../utils';\n\nimport type {\n IDestinationProvider,\n IMetadata,\n ProviderType,\n IAsset,\n TransferStage,\n Protocol,\n} from '../../../../types';\nimport type { IDiagnosticReporter } from '../../../utils/diagnostic';\nimport type { Client, Server, Auth } from '../../../../types/remote/protocol';\nimport type { ILocalStrapiDestinationProviderOptions } from '../local-destination';\nimport { TRANSFER_PATH } from '../../remote/constants';\nimport { ProviderTransferError, ProviderValidationError } from '../../../errors/providers';\n\nexport interface IRemoteStrapiDestinationProviderOptions\n extends Pick<ILocalStrapiDestinationProviderOptions, 'restore' | 'strategy'> {\n url: URL; // the url of the remote Strapi admin\n auth?: Auth.ITransferTokenAuth;\n retryMessageOptions?: {\n retryMessageTimeout: number; // milliseconds to wait for a response from a message\n retryMessageMaxRetries: number; // max number of retries for a message before aborting transfer\n };\n}\n\nconst jsonLength = (obj: object) => Buffer.byteLength(JSON.stringify(obj));\n\nclass RemoteStrapiDestinationProvider implements IDestinationProvider {\n name = 'destination::remote-strapi';\n\n type: ProviderType = 'destination';\n\n options: IRemoteStrapiDestinationProviderOptions;\n\n ws: WebSocket | null;\n\n dispatcher: ReturnType<typeof createDispatcher> | null;\n\n transferID: string | null;\n\n stats!: { [TStage in Exclude<TransferStage, 'schemas'>]: { count: number } };\n\n #diagnostics?: IDiagnosticReporter;\n\n constructor(options: IRemoteStrapiDestinationProviderOptions) {\n this.options = options;\n this.ws = null;\n this.dispatcher = null;\n this.transferID = null;\n\n this.resetStats();\n }\n\n private resetStats() {\n this.stats = {\n assets: { count: 0 },\n entities: { count: 0 },\n links: { count: 0 },\n configuration: { count: 0 },\n };\n }\n\n async initTransfer(): Promise<string> {\n const { strategy, restore } = this.options;\n\n const query = this.dispatcher?.dispatchCommand({\n command: 'init',\n params: { options: { strategy, restore }, transfer: 'push' },\n });\n\n const res = (await query) as Server.Payload<Server.InitMessage>;\n if (!res?.transferID) {\n throw new ProviderTransferError('Init failed, invalid response from the server');\n }\n\n this.resetStats();\n\n return res.transferID;\n }\n\n #startStepOnce(stage: Client.TransferPushStep) {\n return once(() => this.#startStep(stage));\n }\n\n async #startStep<T extends Client.TransferPushStep>(step: T) {\n try {\n await this.dispatcher?.dispatchTransferStep({ action: 'start', step });\n } catch (e) {\n if (e instanceof Error) {\n return e;\n }\n\n if (typeof e === 'string') {\n return new ProviderTransferError(e);\n }\n\n return new ProviderTransferError('Unexpected error');\n }\n\n this.stats[step] = { count: 0 };\n\n return null;\n }\n\n async #endStep<T extends Client.TransferPushStep>(step: T) {\n try {\n const res = await this.dispatcher?.dispatchTransferStep<{\n ok: boolean;\n stats: Protocol.Client.Stats;\n }>({\n action: 'end',\n step,\n });\n\n return { stats: res?.stats ?? null, error: null };\n } catch (e) {\n if (e instanceof Error) {\n return { stats: null, error: e };\n }\n\n if (typeof e === 'string') {\n return { stats: null, error: new ProviderTransferError(e) };\n }\n\n return { stats: null, error: new ProviderTransferError('Unexpected error') };\n }\n }\n\n async #streamStep<T extends Client.TransferPushStep>(\n step: T,\n message: Client.GetTransferPushStreamData<T>\n ) {\n try {\n if (step === 'assets') {\n const assetMessage = message as Protocol.Client.TransferAssetFlow[];\n this.stats[step].count += assetMessage.filter((data) => data.action === 'start').length;\n } else {\n this.stats[step].count += message.length;\n }\n\n await this.dispatcher?.dispatchTransferStep({ action: 'stream', step, data: message });\n } catch (e) {\n if (e instanceof Error) {\n return e;\n }\n\n if (typeof e === 'string') {\n return new ProviderTransferError(e);\n }\n\n return new ProviderTransferError('Unexpected error');\n }\n\n return null;\n }\n\n #writeStream(step: Exclude<Client.TransferPushStep, 'assets'>): Writable {\n type Step = typeof step;\n\n const batchSize = 1024 * 1024; // 1MB;\n conLine truncated