UNPKG

@skyway-sdk/core

Version:

The official Next Generation JavaScript SDK for SkyWay

545 lines 24 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SkyWayChannel = exports.SkyWayChannelImpl = void 0; const common_1 = require("@skyway-sdk/common"); const errors_1 = require("../errors"); const localPerson_1 = require("../member/localPerson"); const factory_1 = require("../publication/factory"); const factory_2 = require("../subscription/factory"); const util_1 = require("../util"); const validation_1 = require("../validation"); const log = new common_1.Logger('packages/core/src/channel/index.ts'); /**@internal */ class SkyWayChannelImpl { constructor( /**@private */ _context, /**@private */ _channelImpl) { this._context = _context; this._channelImpl = _channelImpl; this.id = this._channelImpl.id; this.name = this._channelImpl.name; this.appId = this._context.appId; this.disposed = false; this.config = this._context.config; this._state = 'opened'; this._api = this._context._api; this._members = {}; /**@private */ this._getMember = (id) => this._members[id]; this._publications = {}; /**@private */ this._getPublication = (id) => this._publications[id]; this._subscriptions = {}; /**@private */ this._getSubscription = (id) => this._subscriptions[id]; // events this._events = new common_1.Events(); this.onClosed = this._events.make(); this.onMetadataUpdated = this._events.make(); this.onMemberListChanged = this._events.make(); this.onMemberJoined = this._events.make(); this.onMemberLeft = this._events.make(); this.onMemberMetadataUpdated = this._events.make(); this.onPublicationListChanged = this._events.make(); this.onStreamPublished = this._events.make(); this.onStreamUnpublished = this._events.make(); this.onPublicationMetadataUpdated = this._events.make(); this.onPublicationEnabled = this._events.make(); this.onPublicationDisabled = this._events.make(); this.onSubscriptionListChanged = this._events.make(); this.onPublicationSubscribed = this._events.make(); this.onPublicationUnsubscribed = this._events.make(); /**@private */ this._onDisposed = this._events.make(); this.leave = (member) => __awaiter(this, void 0, void 0, function* () { return this._channelImpl.leave(this.id, member.id); }); this.updateMetadata = (metadata) => this._channelImpl.updateChannelMetadata(metadata); this.close = () => new Promise((r, f) => { if (this.state === 'closed') { f((0, util_1.createError)({ operationName: 'SkyWayChannelImpl.close', path: log.prefix, info: errors_1.errors.alreadyChannelClosed, channel: this, context: this._context, payload: this.toJSON(), })); return; } const executeClose = () => __awaiter(this, void 0, void 0, function* () { const timestamp = log.info('[start] close channel', yield (0, util_1.createLogPayload)({ operationName: 'SkyWayChannelImpl.close', channel: this, })); try { yield this._channelImpl.close().catch((e) => { const error = (0, util_1.createError)({ operationName: 'SkyWayChannelImpl.close', context: this._context, info: Object.assign(Object.assign({}, errors_1.errors.internal), { detail: '_api.deleteChannel failed' }), error: e, path: log.prefix, channel: this, }); throw error; }); if (this._state !== 'closed') { yield this.onClosed .asPromise(this._context.config.rtcApi.timeout) .catch((e) => { const error = (0, util_1.createError)({ operationName: 'SkyWayChannelImpl.close', context: this._context, info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'channel.onClosed' }), error: e, path: log.prefix, channel: this, }); throw error; }); } } catch (error) { log.error(error.message, error); f(error); return; } log.elapsed(timestamp, '[end] close channel', yield (0, util_1.createLogPayload)({ operationName: 'SkyWayChannelImpl.close', channel: this, })); r(); }); executeClose().catch(f); }); /**@private */ this._updateMemberTtl = (memberId, ttlSec) => this._channelImpl.updateMemberTtl(memberId, ttlSec); /**@private */ this._updateMemberMetadata = (memberId, metadata) => this._channelImpl.updateMemberMetadata(memberId, metadata); /**@private */ /**@throws {SkyWayError} */ this._publish = (init) => this._channelImpl.publish(init); /**@private */ this._unpublish = (publicationId) => __awaiter(this, void 0, void 0, function* () { return this._channelImpl.unpublish(publicationId); }); /**@private * @throws {@link SkyWayError} */ this._subscribe = (subscriberId, publicationId) => { const publication = this._getPublication(publicationId); const subscriber = this._getMember(subscriberId); if (subscriber === undefined || subscriber === null) { throw (0, util_1.createError)({ operationName: 'SkyWayChannelImpl._subscribe', path: log.prefix, info: Object.assign(Object.assign({}, errors_1.errors.internal), { detail: 'subscriber not found' }), channel: this, context: this._context, payload: { subscriberId, publicationId }, }); } return this._channelImpl.subscribe({ publication: publication.toJSON(), subscriber: subscriber.toJSON(), }); }; /**@private */ this._unsubscribe = (subscriptionId) => __awaiter(this, void 0, void 0, function* () { if (!this._getSubscription(subscriptionId)) { throw (0, util_1.createError)({ operationName: 'SkyWayChannelImpl._unsubscribe', path: log.prefix, info: Object.assign(Object.assign({}, errors_1.errors.internal), { detail: "can't unsubscribe not exist subscription" }), channel: this, context: this._context, payload: { subscriptionId }, }); } yield this._channelImpl.unsubscribe(subscriptionId); }); /**@private */ this._updatePublicationMetadata = (publicationId, metadata) => this._channelImpl.updatePublicationMetadata(publicationId, metadata); /**@private */ this._disablePublication = (publicationId) => this._channelImpl.disablePublication(publicationId); /**@private */ this._enablePublication = (publicationId) => this._channelImpl.enablePublication(publicationId); this._setupPropertiesFromChannel(); this._setupListenChannelEvent(); _context.onDisposed.once(() => { this.dispose(); }); log.debug('channel spawned', this.toJSON()); } _addLocalPerson(member, config) { return __awaiter(this, void 0, void 0, function* () { const person = yield (0, localPerson_1.createLocalPerson)(this._context, this, member, config); this._localPerson = person; this._members[this._localPerson.id] = person; return person; }); } _addRemoteMember(memberDto) { const exist = this._getMember(memberDto.id); if (exist) { if (exist.side === 'local') { throw (0, util_1.createError)({ operationName: 'SkyWayChannelImpl._addRemoteMember', path: log.prefix, context: this._context, channel: this, info: errors_1.errors.internal, }); } return exist; } const member = this._context._createRemoteMember(this, memberDto); this._members[member.id] = member; return member; } _removeMember(memberId) { delete this._members[memberId]; const isLocalPerson = this._localPerson && this._localPerson.id === memberId; if (isLocalPerson) { this._localPerson = undefined; } } /**@private */ _addPublication(p) { const exist = this._getPublication(p.id); if (exist) { return exist; } const publication = (0, factory_1.createPublication)(this, p); this._publications[p.id] = publication; return publication; } _removePublication(publicationId) { delete this._publications[publicationId]; } /**@private */ _addSubscription(s) { const exist = this._getSubscription(s.id); if (exist) { return exist; } const subscription = (0, factory_2.createSubscription)(this, s); this._subscriptions[s.id] = subscription; return subscription; } _removeSubscription(subscriptionId) { delete this._subscriptions[subscriptionId]; } get localPerson() { return this._localPerson; } get members() { return Object.values(this._members); } get bots() { return this.members.filter((m) => m.type === 'bot'); } get publications() { return Object.values(this._publications); } get subscriptions() { return Object.values(this._subscriptions); } get metadata() { return this._channelImpl.metadata; } get state() { return this._state; } toJSON() { return { id: this.id, name: this.name, appId: this.appId, metadata: this.metadata, members: this.members, publications: this.publications, subscriptions: this.subscriptions, }; } _setupPropertiesFromChannel() { this._channelImpl.members.forEach((memberDto) => { this._addRemoteMember(memberDto); }); this._channelImpl.publications.forEach((publicationDto) => { this._addPublication(publicationDto); }); this._channelImpl.subscriptions.forEach((subscriptionDto) => { this._addSubscription(subscriptionDto); }); } _setupListenChannelEvent() { this._channelImpl.onClosed.add(() => this._handleOnChannelClose()); this._channelImpl.onMetadataUpdated.add(({ channel }) => this._handleOnChannelMetadataUpdate(channel.metadata)); this._channelImpl.onMemberJoined.add(({ member }) => { this._handleOnMemberJoin(member); }); this._channelImpl.onMemberLeft.add(({ member }) => { this._handleOnMemberLeft(member); }); this._channelImpl.onMemberListChanged.pipe(this.onMemberListChanged); this._channelImpl.onMemberMetadataUpdated.add(({ member }) => { this._handleOnMemberMetadataUpdate(member, member.metadata); }); this._channelImpl.onStreamPublished.add(({ publication }) => { this._handleOnStreamPublish(publication); }); this._channelImpl.onStreamUnpublished.add(({ publication }) => { this._handleOnStreamUnpublish(publication); }); this._channelImpl.onPublicationListChanged.pipe(this.onPublicationListChanged); this._channelImpl.onPublicationMetadataUpdated.add(({ publication }) => { this._handleOnPublicationMetadataUpdate(publication, publication.metadata); }); this._channelImpl.onPublicationEnabled.add(({ publication }) => __awaiter(this, void 0, void 0, function* () { return yield this._handleOnPublicationEnabled(publication); })); this._channelImpl.onPublicationDisabled.add(({ publication }) => __awaiter(this, void 0, void 0, function* () { return yield this._handleOnPublicationDisabled(publication); })); this._channelImpl.onPublicationSubscribed.add(({ subscription }) => { this._handleOnStreamSubscribe(subscription); }); this._channelImpl.onPublicationUnsubscribed.add(({ subscription }) => { this._handleOnStreamUnsubscribe(subscription); }); this._channelImpl.onSubscriptionListChanged.pipe(this.onSubscriptionListChanged); } _handleOnChannelClose() { this._state = 'closed'; this.onClosed.emit({}); this.dispose(); } _handleOnChannelMetadataUpdate(metadata) { this.onMetadataUpdated.emit({ metadata }); } _handleOnMemberJoin(memberDto) { const member = this._addRemoteMember(memberDto); this.onMemberJoined.emit({ member }); } _handleOnMemberLeft(memberDto) { const member = this._getMember(memberDto.id); this._removeMember(member.id); member._left(); this.onMemberLeft.emit({ member }); } _handleOnMemberMetadataUpdate(memberDto, metadata) { var _a; const member = this._getMember(memberDto.id); member._metadataUpdated(metadata); if (((_a = this.localPerson) === null || _a === void 0 ? void 0 : _a.id) === memberDto.id) { this.localPerson._metadataUpdated(metadata); } this.onMemberMetadataUpdated.emit({ member, metadata }); } _handleOnStreamPublish(publicationDto) { const publication = this._addPublication(publicationDto); this.onStreamPublished.emit({ publication }); } _handleOnStreamUnpublish(publicationDto) { const publication = this._getPublication(publicationDto.id); this._removePublication(publication.id); publication._unpublished(); this.onStreamUnpublished.emit({ publication }); } _handleOnPublicationMetadataUpdate(publicationDto, metadata) { const publication = this._getPublication(publicationDto.id); publication._updateMetadata(metadata); this.onPublicationMetadataUpdated.emit({ publication, metadata }); } _handleOnPublicationEnabled(publicationDto) { return __awaiter(this, void 0, void 0, function* () { const publication = this._getPublication(publicationDto.id); publication._enable(); this.onPublicationEnabled.emit({ publication }); }); } _handleOnPublicationDisabled(publicationDto) { return __awaiter(this, void 0, void 0, function* () { const publication = this._getPublication(publicationDto.id); yield publication._disable(); this.onPublicationDisabled.emit({ publication }); }); } _handleOnStreamSubscribe(subscriptionDto) { const subscription = this._addSubscription(subscriptionDto); const publication = this._getPublication(subscription.publication.id); publication._subscribed(subscription); this.onPublicationSubscribed.emit({ subscription }); } _handleOnStreamUnsubscribe(subscriptionDto) { const subscription = this._getSubscription(subscriptionDto.id); this._removeSubscription(subscription.id); subscription._canceled(); const publication = this._getPublication(subscription.publication.id); publication._unsubscribed(subscription); this.onPublicationUnsubscribed.emit({ subscription }); } join(options = {}) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function* () { const timestamp = log.info('[start] join', yield (0, util_1.createLogPayload)({ operationName: 'SkyWayChannelImpl.join', channel: this, })); if (this._localPerson) { throw (0, util_1.createError)({ operationName: 'SkyWayChannelImpl.join', path: log.prefix, info: errors_1.errors.alreadyLocalPersonExist, channel: this, context: this._context, }); } if (!(0, validation_1.isValidName)(options.name)) { throw (0, util_1.createError)({ operationName: 'SkyWayChannelImpl.join', path: log.prefix, info: errors_1.errors.invalidRequestParameter, channel: this, context: this._context, payload: options, }); } if (options.name !== undefined && options.name !== null) { const exist = this.members.find((m) => m.name === options.name); if (exist) { throw (0, util_1.createError)({ operationName: 'SkyWayChannelImpl.join', path: log.prefix, info: errors_1.errors.alreadySameNameMemberExist, channel: this, context: this._context, payload: options, }); } } (_a = options.keepaliveIntervalSec) !== null && _a !== void 0 ? _a : (options.keepaliveIntervalSec = this.config.member.keepaliveIntervalSec); (_b = options.keepaliveIntervalGapSec) !== null && _b !== void 0 ? _b : (options.keepaliveIntervalGapSec = this.config.member.keepaliveIntervalGapSec); (_c = options.preventAutoLeaveOnBeforeUnload) !== null && _c !== void 0 ? _c : (options.preventAutoLeaveOnBeforeUnload = this.config.member.preventAutoLeaveOnBeforeUnload); const init = Object.assign(Object.assign({}, options), { type: 'person', subtype: 'person' }); if (options.keepaliveIntervalSec !== null) { init.ttlSec = (yield this._context._api.getServerUnixtimeInSec()) + options.keepaliveIntervalSec; } const member = yield this._channelImpl.joinChannel(init).catch((e) => { log.error('[failed] join', e); throw e; }); log.elapsed(timestamp, '[elapsed] join / channelImpl.joinChannel', { member, }); const person = yield this._addLocalPerson(member, options); const adapter = new localPerson_1.LocalPersonAdapter(person); log.elapsed(timestamp, '[end] join', { person }); return adapter; }); } dispose() { if (this.disposed) { return; } this.disposed = true; log.debug('disposed', this.toJSON()); this._channelImpl.dispose(); this._onDisposed.emit(); this._events.dispose(); } } exports.SkyWayChannelImpl = SkyWayChannelImpl; class SkyWayChannel { /** * @description [japanese] Channelの作成 */ static Create(context, init = {}) { return __awaiter(this, void 0, void 0, function* () { const timestamp = log.info('[start] createChannel', { operationName: 'SkyWayChannel.Create', }); if (!(0, validation_1.isValidName)(init.name)) { throw (0, util_1.createError)({ operationName: 'SkyWayChannel.Create', info: errors_1.errors.invalidRequestParameter, path: log.prefix, context, payload: init, }); } const channelImpl = yield context._api.createChannel(init).catch((e) => { log.error('[failed] createChannel', e); throw e; }); const channel = new SkyWayChannelImpl(context, channelImpl); log.elapsed(timestamp, '[end] createChannel'); return channel; }); } /** * @description [japanese] 既存のChannelの取得 */ static Find(context, query) { return __awaiter(this, void 0, void 0, function* () { const timestamp = log.info('[start] findChannel', { operationName: 'SkyWayChannel.Find', }); // id が指定されていない場合に channelName の validation を行う if (query.id === undefined && !(0, validation_1.isValidName)(query.name)) { throw (0, util_1.createError)({ operationName: 'SkyWayChannel.Find', info: errors_1.errors.invalidRequestParameter, path: log.prefix, context, payload: query, }); } const channelImpl = yield context._api.findChannel(query).catch((e) => { log.error('[failed] findChannel', e); throw e; }); const channel = new SkyWayChannelImpl(context, channelImpl); log.elapsed(timestamp, '[end] findChannel'); return channel; }); } /** * @description [japanese] Channelの取得を試み、存在しなければ作成する */ static FindOrCreate(context, query) { return __awaiter(this, void 0, void 0, function* () { const timestamp = log.info('[start] findOrCreateChannel', { operationName: 'SkyWayChannel.FindOrCreate', }); if (!(0, validation_1.isValidName)(query.name)) { throw (0, util_1.createError)({ operationName: 'SkyWayChannel.Create', info: errors_1.errors.invalidRequestParameter, path: log.prefix, context, payload: query, }); } const channelImpl = yield context._api .findOrCreateChannel(query) .catch((e) => { log.error('[failed] findOrCreateChannel', e); throw e; }); const channel = new SkyWayChannelImpl(context, channelImpl); log.elapsed(timestamp, '[end] findOrCreateChannel'); return channel; }); } constructor() { } } exports.SkyWayChannel = SkyWayChannel; //# sourceMappingURL=index.js.map