@lemoncloud/chatic-sockets-lib 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/client-socket-v2/gateways/channel-gateway.d.ts +1 -1
  2. package/dist/client-socket-v2/gateways/cloud-gateway.d.ts +7 -1
  3. package/dist/client-socket-v2/gateways/cloud-gateway.js +3 -0
  4. package/dist/client-socket-v2/gateways/place-gateway.d.ts +18 -0
  5. package/dist/client-socket-v2/gateways/place-gateway.js +14 -0
  6. package/dist/client-socket-v2/gateways/profile-gateway.d.ts +18 -0
  7. package/dist/client-socket-v2/gateways/profile-gateway.js +14 -0
  8. package/dist/client-socket-v2/gateways/user-gateway.d.ts +20 -8
  9. package/dist/client-socket-v2/gateways/user-gateway.js +1 -1
  10. package/dist/client-socket-v2/index.d.ts +5 -0
  11. package/dist/client-socket-v2/index.js +5 -0
  12. package/dist/client-socket-v2/plans/channel-sync-plan.d.ts +33 -0
  13. package/dist/client-socket-v2/plans/channel-sync-plan.js +66 -0
  14. package/dist/client-socket-v2/plans/device-sync-plan.d.ts +5 -1
  15. package/dist/client-socket-v2/plans/device-sync-plan.js +6 -3
  16. package/dist/client-socket-v2/plans/place-sync-plan.d.ts +33 -0
  17. package/dist/client-socket-v2/plans/place-sync-plan.js +67 -0
  18. package/dist/client-socket-v2/plans/profile-sync-plan.d.ts +33 -0
  19. package/dist/client-socket-v2/plans/profile-sync-plan.js +67 -0
  20. package/dist/client-socket-v2/socket-runtime.d.ts +5 -1
  21. package/dist/client-socket-v2/socket-runtime.js +5 -1
  22. package/dist/client-socket-v2/sync-scheduler.d.ts +15 -1
  23. package/dist/client-socket-v2/sync-scheduler.js +89 -12
  24. package/dist/client-socket-v2/types.d.ts +22 -0
  25. package/dist/lib/channel/types.d.ts +27 -11
  26. package/dist/lib/cloud/types.d.ts +38 -2
  27. package/dist/lib/device/contracts.d.ts +4 -0
  28. package/dist/lib/device/types.d.ts +3 -1
  29. package/dist/lib/place/types.d.ts +53 -0
  30. package/dist/lib/place/types.js +2 -0
  31. package/dist/lib/profile/types.d.ts +55 -0
  32. package/dist/lib/profile/types.js +2 -0
  33. package/dist/lib/socket-actions.d.ts +55 -3
  34. package/dist/lib/socket-actions.js +44 -2
  35. package/dist/lib/socket-inputs.d.ts +3 -1
  36. package/dist/modules/chat/model.d.ts +107 -0
  37. package/dist/modules/chat/model.js +28 -0
  38. package/dist/modules/chat/views.d.ts +50 -0
  39. package/dist/modules/chat/views.js +23 -0
  40. package/dist/modules/sockets/model.d.ts +206 -0
  41. package/dist/modules/sockets/model.js +28 -0
  42. package/dist/modules/sockets/types.d.ts +100 -0
  43. package/dist/modules/sockets/types.js +86 -0
  44. package/dist/modules/sockets/views.d.ts +67 -0
  45. package/dist/modules/sockets/views.js +23 -0
  46. package/package.json +4 -2
@@ -32,7 +32,7 @@ export interface ChannelGateway {
32
32
  sync<T = unknown>(data?: ChannelSyncInput | null): Promise<T>;
33
33
  /** $socials.ChannelUsersSyncView */
34
34
  syncUsers<T = unknown>(data?: ChannelSyncUsersInput | null): Promise<T>;
35
- /** $socials.SiteProfileSyncView */
35
+ /** @deprecated moved to the profile domain. Use `createProfileGateway(client).sync()`. */
36
36
  syncProfile<T = unknown>(data?: ChannelSyncProfileInput | null): Promise<T>;
37
37
  }
38
38
  export declare const createChannelGateway: (client: ClientSocketV2) => ChannelGateway;
@@ -1,4 +1,4 @@
1
- import type { CloudUpdateInput } from '../../lib/cloud/types';
1
+ import type { CloudCreateInput, CloudDeleteInput, CloudGetInput, CloudUpdateInput } from '../../lib/cloud/types';
2
2
  import type { ClientSocketV2 } from '../types';
3
3
  /**
4
4
  * cloud 도메인 게이트웨이.
@@ -6,7 +6,13 @@ import type { ClientSocketV2 } from '../types';
6
6
  * - 미주입 시 unknown. 주석의 $backend.<View> 가 실제 응답 타입.
7
7
  */
8
8
  export interface CloudGateway {
9
+ /** $backend.CloudView — 미구현(500 NOT IMPLEMENTED) */
10
+ create<T = unknown>(data: CloudCreateInput): Promise<T>;
11
+ /** $backend.CloudView */
12
+ get<T = unknown>(data: CloudGetInput): Promise<T>;
9
13
  /** $backend.CloudView */
10
14
  update<T = unknown>(data: CloudUpdateInput): Promise<T>;
15
+ /** $backend.CloudView */
16
+ delete<T = unknown>(data: CloudDeleteInput): Promise<T>;
11
17
  }
12
18
  export declare const createCloudGateway: (client: ClientSocketV2) => CloudGateway;
@@ -5,7 +5,10 @@ const create_domain_gateway_1 = require("./create-domain-gateway");
5
5
  const createCloudGateway = (client) => {
6
6
  const gateway = (0, create_domain_gateway_1.createDomainGateway)('cloud', client);
7
7
  return {
8
+ create: (data) => gateway.request('create', data),
9
+ get: (data) => gateway.request('get', data),
8
10
  update: (data) => gateway.request('update', data),
11
+ delete: (data) => gateway.request('delete', data),
9
12
  };
10
13
  };
11
14
  exports.createCloudGateway = createCloudGateway;
@@ -0,0 +1,18 @@
1
+ import type { PlaceCreateInput, PlaceDeleteInput, PlaceGetInput, PlaceUpdateInput } from '../../lib/place/types';
2
+ import type { ClientSocketV2 } from '../types';
3
+ /**
4
+ * place(site) 도메인 게이트웨이.
5
+ * - 응답은 @lemoncloud/chatic-backend-api 소유(pass-through)이므로 제네릭 T로 호출부에서 주입한다.
6
+ * - 미주입 시 unknown. 주석의 $backend.<View> 가 실제 응답 타입.
7
+ */
8
+ export interface PlaceGateway {
9
+ /** $backend.MyUserView */
10
+ create<T = unknown>(data: PlaceCreateInput): Promise<T>;
11
+ /** $backend.MySiteView */
12
+ get<T = unknown>(data: PlaceGetInput): Promise<T>;
13
+ /** $backend.MySiteView */
14
+ update<T = unknown>(data: PlaceUpdateInput): Promise<T>;
15
+ /** $backend.MySiteView */
16
+ delete<T = unknown>(data: PlaceDeleteInput): Promise<T>;
17
+ }
18
+ export declare const createPlaceGateway: (client: ClientSocketV2) => PlaceGateway;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createPlaceGateway = void 0;
4
+ const create_domain_gateway_1 = require("./create-domain-gateway");
5
+ const createPlaceGateway = (client) => {
6
+ const gateway = (0, create_domain_gateway_1.createDomainGateway)('place', client);
7
+ return {
8
+ create: (data) => gateway.request('create', data),
9
+ get: (data) => gateway.request('get', data),
10
+ update: (data) => gateway.request('update', data),
11
+ delete: (data) => gateway.request('delete', data),
12
+ };
13
+ };
14
+ exports.createPlaceGateway = createPlaceGateway;
@@ -0,0 +1,18 @@
1
+ import type { ProfileGetInput, ProfileGetMineInput, ProfileSetInput, ProfileSyncInput } from '../../lib/profile/types';
2
+ import type { ClientSocketV2 } from '../types';
3
+ /**
4
+ * profile(site-profile) 도메인 게이트웨이.
5
+ * - 응답은 @lemoncloud/chatic-socials-api 소유(pass-through)이므로 제네릭 T로 호출부에서 주입한다.
6
+ * - 미주입 시 unknown. 주석의 $socials.<View> 가 실제 응답 타입.
7
+ */
8
+ export interface ProfileGateway {
9
+ /** $socials.ProfileView — id 기반 단건 조회 */
10
+ get<T = unknown>(data: ProfileGetInput): Promise<T>;
11
+ /** $socials.ProfileView — 현재 세션 기반 조회 */
12
+ getMine<T = unknown>(data?: ProfileGetMineInput | null): Promise<T>;
13
+ /** $socials.ProfileView */
14
+ set<T = unknown>(data: ProfileSetInput): Promise<T>;
15
+ /** $socials.SiteProfileSyncView — 사이트 멀티프로필 delta 동기화 */
16
+ sync<T = unknown>(data?: ProfileSyncInput | null): Promise<T>;
17
+ }
18
+ export declare const createProfileGateway: (client: ClientSocketV2) => ProfileGateway;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createProfileGateway = void 0;
4
+ const create_domain_gateway_1 = require("./create-domain-gateway");
5
+ const createProfileGateway = (client) => {
6
+ const gateway = (0, create_domain_gateway_1.createDomainGateway)('profile', client);
7
+ return {
8
+ get: (data) => gateway.request('get', data),
9
+ getMine: (data) => gateway.request('get-mine', data !== null && data !== void 0 ? data : null),
10
+ set: (data) => gateway.request('set', data),
11
+ sync: (data) => gateway.request('sync', data !== null && data !== void 0 ? data : null),
12
+ };
13
+ };
14
+ exports.createProfileGateway = createProfileGateway;
@@ -6,21 +6,33 @@ import type { ClientSocketV2 } from '../types';
6
6
  * - 미주입 시 unknown. 각 메서드 주석의 $socials/$backend.<View> 가 실제 응답 타입(서버가 unknown으로 둔 것은 표기 생략).
7
7
  */
8
8
  export interface UserGateway {
9
- /** $socials.ProfileView */
10
- getSiteProfile<T = unknown>(data?: UserGetSiteProfileInput | null): Promise<T>;
9
+ /** $socials.UserView */
10
+ update<T = unknown>(data: UserUpdateProfileInput): Promise<T>;
11
11
  /** ListResult<$backend.MySiteView> */
12
12
  mySite<T = unknown>(data?: UserMySiteInput | null): Promise<T>;
13
- /** $backend.MySiteView */
14
- makeSite<T = unknown>(data: UserMakeSiteInput): Promise<T>;
15
13
  /** $backend.MyInviteView */
16
14
  invite<T = unknown>(data: UserInviteInput): Promise<T>;
17
15
  /** ListResult<$backend.MyInviteView> */
18
16
  inviteBatch<T = unknown>(data: UserInviteBatchInput): Promise<T>;
19
- /** $socials.UserView */
20
- updateProfile<T = unknown>(data: UserUpdateProfileInput): Promise<T>;
21
- /** $backend.MySiteView */
17
+ /**
18
+ * $backend.MySiteView
19
+ * @deprecated moved to the place domain. Use `createPlaceGateway(client).create()`.
20
+ */
21
+ makeSite<T = unknown>(data: UserMakeSiteInput): Promise<T>;
22
+ /**
23
+ * $backend.MySiteView
24
+ * @deprecated moved to the place domain. Use `createPlaceGateway(client).update()`.
25
+ */
22
26
  updateSite<T = unknown>(data: UserUpdateSiteInput): Promise<T>;
23
- /** $socials.ProfileView */
27
+ /**
28
+ * $socials.ProfileView
29
+ * @deprecated moved to the profile domain. Use `createProfileGateway(client).getMine()`.
30
+ */
31
+ getSiteProfile<T = unknown>(data?: UserGetSiteProfileInput | null): Promise<T>;
32
+ /**
33
+ * $socials.ProfileView
34
+ * @deprecated moved to the profile domain. Use `createProfileGateway(client).set()`.
35
+ */
24
36
  setSiteProfile<T = unknown>(data: UserSetSiteProfileInput): Promise<T>;
25
37
  }
26
38
  export declare const createUserGateway: (client: ClientSocketV2) => UserGateway;
@@ -10,7 +10,7 @@ const createUserGateway = (client) => {
10
10
  makeSite: (data) => gateway.request('make-site', data),
11
11
  invite: (data) => gateway.request('invite', data),
12
12
  inviteBatch: (data) => gateway.request('invite-batch', data),
13
- updateProfile: (data) => gateway.request('update-profile', data),
13
+ update: (data) => gateway.request('update-profile', data),
14
14
  updateSite: (data) => gateway.request('update-site', data),
15
15
  setSiteProfile: (data) => gateway.request('set-site-profile', data),
16
16
  };
@@ -17,6 +17,9 @@ export * from './sync-scheduler';
17
17
  export * from './socket-runtime';
18
18
  export * from './create-device-runtime';
19
19
  export * from './plans/device-sync-plan';
20
+ export * from './plans/channel-sync-plan';
21
+ export * from './plans/place-sync-plan';
22
+ export * from './plans/profile-sync-plan';
20
23
  export * from './gateways/create-domain-gateway';
21
24
  export * from './gateways/device-gateway';
22
25
  export * from './gateways/channel-gateway';
@@ -24,6 +27,8 @@ export * from './gateways/auth-gateway';
24
27
  export * from './gateways/chat-gateway';
25
28
  export * from './gateways/cloud-gateway';
26
29
  export * from './gateways/user-gateway';
30
+ export * from './gateways/place-gateway';
31
+ export * from './gateways/profile-gateway';
27
32
  export * from '../lib/types';
28
33
  export * from '../lib/device/types';
29
34
  export * from '../lib/device/contracts';
@@ -35,6 +35,9 @@ __exportStar(require("./sync-scheduler"), exports);
35
35
  __exportStar(require("./socket-runtime"), exports);
36
36
  __exportStar(require("./create-device-runtime"), exports);
37
37
  __exportStar(require("./plans/device-sync-plan"), exports);
38
+ __exportStar(require("./plans/channel-sync-plan"), exports);
39
+ __exportStar(require("./plans/place-sync-plan"), exports);
40
+ __exportStar(require("./plans/profile-sync-plan"), exports);
38
41
  __exportStar(require("./gateways/create-domain-gateway"), exports);
39
42
  __exportStar(require("./gateways/device-gateway"), exports);
40
43
  __exportStar(require("./gateways/channel-gateway"), exports);
@@ -42,6 +45,8 @@ __exportStar(require("./gateways/auth-gateway"), exports);
42
45
  __exportStar(require("./gateways/chat-gateway"), exports);
43
46
  __exportStar(require("./gateways/cloud-gateway"), exports);
44
47
  __exportStar(require("./gateways/user-gateway"), exports);
48
+ __exportStar(require("./gateways/place-gateway"), exports);
49
+ __exportStar(require("./gateways/profile-gateway"), exports);
45
50
  __exportStar(require("../lib/types"), exports);
46
51
  __exportStar(require("../lib/device/types"), exports);
47
52
  __exportStar(require("../lib/device/contracts"), exports);
@@ -0,0 +1,33 @@
1
+ import type { ChannelView } from '../../modules/chat/views';
2
+ import type { SocketMessage } from '../../lib/types';
3
+ import type { DomainSyncContext, DomainSyncPlan, SyncBackoffOptions, SyncFailureInfo, SyncTargetDescriptor } from '../types';
4
+ export interface ChannelSyncTarget extends SyncTargetDescriptor {
5
+ type: 'channel';
6
+ }
7
+ export interface ChannelSyncSnapshot {
8
+ id?: string;
9
+ updatedAt?: number;
10
+ view?: ChannelView;
11
+ }
12
+ export interface ChannelSyncPlanOptions {
13
+ intervalMs?: number;
14
+ idleBackoff?: SyncBackoffOptions;
15
+ resetSnapshotOnConnected?: boolean;
16
+ onUpdate?: (target: ChannelSyncTarget, view: ChannelView, previous?: ChannelSyncSnapshot) => void;
17
+ onRemove?: (target: ChannelSyncTarget, previous?: ChannelSyncSnapshot) => void;
18
+ }
19
+ /** Single-channel sync: polls `channel.get`, applies on `updatedAt` change, re-pulls on a `channel.sync` nudge. */
20
+ export declare class ChannelSyncPlan implements DomainSyncPlan<ChannelSyncTarget> {
21
+ private readonly options;
22
+ readonly domain = "channel";
23
+ readonly idleBackoff: SyncBackoffOptions;
24
+ constructor(options?: ChannelSyncPlanOptions);
25
+ supports: (target: SyncTargetDescriptor) => target is ChannelSyncTarget;
26
+ getKey: (target: ChannelSyncTarget) => string;
27
+ getIntervalMs: (target: ChannelSyncTarget) => number;
28
+ onConnected: (target: ChannelSyncTarget, ctx: DomainSyncContext) => void;
29
+ run: (target: ChannelSyncTarget, ctx: DomainSyncContext) => Promise<void>;
30
+ onTrigger: (target: ChannelSyncTarget, message: SocketMessage<any>, ctx: DomainSyncContext) => Promise<void>;
31
+ onStopped: (target: ChannelSyncTarget, _info: SyncFailureInfo, ctx: DomainSyncContext) => void;
32
+ updateLocalState: (target: ChannelSyncTarget, snapshot: unknown, ctx: DomainSyncContext) => void;
33
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ChannelSyncPlan = void 0;
13
+ /** Single-channel sync: polls `channel.get`, applies on `updatedAt` change, re-pulls on a `channel.sync` nudge. */
14
+ class ChannelSyncPlan {
15
+ constructor(options = {}) {
16
+ var _a;
17
+ this.options = options;
18
+ this.domain = 'channel';
19
+ this.supports = (target) => (target === null || target === void 0 ? void 0 : target.type) === 'channel';
20
+ this.getKey = (target) => { var _a; return `channel:${(_a = target === null || target === void 0 ? void 0 : target.id) !== null && _a !== void 0 ? _a : ''}`; };
21
+ this.getIntervalMs = (target) => { var _a, _b; return (_b = (_a = target.intervalMs) !== null && _a !== void 0 ? _a : this.options.intervalMs) !== null && _b !== void 0 ? _b : 2000; };
22
+ this.onConnected = (target, ctx) => {
23
+ if (this.options.resetSnapshotOnConnected === false)
24
+ return;
25
+ ctx.writeSnapshot(target, undefined);
26
+ };
27
+ this.run = (target, ctx) => __awaiter(this, void 0, void 0, function* () {
28
+ var _b, _c, _d, _e;
29
+ if (!target.id)
30
+ return;
31
+ const prev = ctx.readSnapshot(target);
32
+ const input = { id: target.id };
33
+ const view = yield ctx.client.request('channel.get', input);
34
+ const nextUpdatedAt = typeof (view === null || view === void 0 ? void 0 : view.updatedAt) === 'number' ? view.updatedAt : undefined;
35
+ const prevUpdatedAt = typeof (prev === null || prev === void 0 ? void 0 : prev.updatedAt) === 'number' ? prev.updatedAt : undefined;
36
+ if (prevUpdatedAt === nextUpdatedAt && (prev === null || prev === void 0 ? void 0 : prev.view))
37
+ return;
38
+ const next = {
39
+ id: `${(_c = (_b = view === null || view === void 0 ? void 0 : view.id) !== null && _b !== void 0 ? _b : target.id) !== null && _c !== void 0 ? _c : ''}` || undefined,
40
+ updatedAt: nextUpdatedAt,
41
+ view,
42
+ };
43
+ ctx.writeSnapshot(target, next);
44
+ (_e = (_d = this.options).onUpdate) === null || _e === void 0 ? void 0 : _e.call(_d, target, view, prev);
45
+ });
46
+ this.onTrigger = (target, message, ctx) => __awaiter(this, void 0, void 0, function* () {
47
+ const data = ((message === null || message === void 0 ? void 0 : message.data) || {});
48
+ if (target.id && (data === null || data === void 0 ? void 0 : data.id) && target.id !== data.id)
49
+ return;
50
+ yield this.run(target, ctx);
51
+ });
52
+ this.onStopped = (target, _info, ctx) => {
53
+ var _a, _b;
54
+ const prev = ctx.readSnapshot(target);
55
+ (_b = (_a = this.options).onRemove) === null || _b === void 0 ? void 0 : _b.call(_a, target, prev);
56
+ };
57
+ this.updateLocalState = (target, snapshot, ctx) => {
58
+ var _a, _b;
59
+ const prev = ctx.readSnapshot(target);
60
+ const patch = (snapshot || {});
61
+ ctx.writeSnapshot(target, Object.assign(Object.assign(Object.assign({}, prev), patch), { view: (_a = patch.view) !== null && _a !== void 0 ? _a : prev === null || prev === void 0 ? void 0 : prev.view, updatedAt: (_b = patch.updatedAt) !== null && _b !== void 0 ? _b : prev === null || prev === void 0 ? void 0 : prev.updatedAt }));
62
+ };
63
+ this.idleBackoff = (_a = options.idleBackoff) !== null && _a !== void 0 ? _a : { factor: 2, maxMs: 60000 };
64
+ }
65
+ }
66
+ exports.ChannelSyncPlan = ChannelSyncPlan;
@@ -1,6 +1,6 @@
1
1
  import type { DeviceGetResponseData } from '../../lib/device/types';
2
2
  import type { SocketMessage } from '../../lib/types';
3
- import type { DomainSyncContext, DomainSyncPlan, SyncTargetDescriptor } from '../types';
3
+ import type { DomainSyncContext, DomainSyncPlan, SyncBackoffOptions, SyncFailurePolicy, SyncTargetDescriptor } from '../types';
4
4
  export interface DeviceSyncTarget extends SyncTargetDescriptor {
5
5
  type: 'device';
6
6
  }
@@ -14,11 +14,15 @@ export interface DeviceSyncPlanOptions {
14
14
  intervalMs?: number;
15
15
  sendSyncHint?: boolean;
16
16
  resetSnapshotOnConnected?: boolean;
17
+ failurePolicy?: SyncFailurePolicy;
18
+ idleBackoff?: SyncBackoffOptions;
17
19
  onUpdate?: (target: DeviceSyncTarget, view: DeviceGetResponseData, previous?: DeviceSyncSnapshot) => void;
18
20
  }
19
21
  export declare class DeviceSyncPlan implements DomainSyncPlan<DeviceSyncTarget> {
20
22
  private readonly options;
21
23
  readonly domain = "device";
24
+ readonly failurePolicy: SyncFailurePolicy;
25
+ readonly idleBackoff: SyncBackoffOptions;
22
26
  constructor(options?: DeviceSyncPlanOptions);
23
27
  supports: (target: SyncTargetDescriptor) => target is DeviceSyncTarget;
24
28
  getKey: (target: DeviceSyncTarget) => string;
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.DeviceSyncPlan = void 0;
13
13
  class DeviceSyncPlan {
14
14
  constructor(options = {}) {
15
+ var _a, _b;
15
16
  this.options = options;
16
17
  this.domain = 'device';
17
18
  this.supports = (target) => (target === null || target === void 0 ? void 0 : target.type) === 'device';
@@ -23,7 +24,7 @@ class DeviceSyncPlan {
23
24
  ctx.writeSnapshot(target, undefined);
24
25
  };
25
26
  this.run = (target, ctx) => __awaiter(this, void 0, void 0, function* () {
26
- var _a, _b, _c, _d;
27
+ var _c, _d, _e, _f;
27
28
  const prev = ctx.readSnapshot(target);
28
29
  if (this.options.sendSyncHint !== false) {
29
30
  const syncData = Object.assign(Object.assign({}, (target.id ? { id: target.id } : {})), (typeof (prev === null || prev === void 0 ? void 0 : prev.tick) === 'number' ? { tick: prev.tick } : {}));
@@ -39,13 +40,13 @@ class DeviceSyncPlan {
39
40
  if (prevTick === nextTick && (prev === null || prev === void 0 ? void 0 : prev.view))
40
41
  return;
41
42
  const next = {
42
- id: `${(_b = (_a = view === null || view === void 0 ? void 0 : view.id) !== null && _a !== void 0 ? _a : target.id) !== null && _b !== void 0 ? _b : ''}` || undefined,
43
+ id: `${(_d = (_c = view === null || view === void 0 ? void 0 : view.id) !== null && _c !== void 0 ? _c : target.id) !== null && _d !== void 0 ? _d : ''}` || undefined,
43
44
  tick: nextTick,
44
45
  lastAppliedTick: nextTick,
45
46
  view,
46
47
  };
47
48
  ctx.writeSnapshot(target, next);
48
- (_d = (_c = this.options).onUpdate) === null || _d === void 0 ? void 0 : _d.call(_c, target, view, prev);
49
+ (_f = (_e = this.options).onUpdate) === null || _f === void 0 ? void 0 : _f.call(_e, target, view, prev);
49
50
  });
50
51
  this.onTrigger = (target, message, ctx) => __awaiter(this, void 0, void 0, function* () {
51
52
  const data = ((message === null || message === void 0 ? void 0 : message.data) || {});
@@ -59,6 +60,8 @@ class DeviceSyncPlan {
59
60
  const patch = (snapshot || {});
60
61
  ctx.writeSnapshot(target, Object.assign(Object.assign(Object.assign({}, prev), patch), { view: (_a = patch.view) !== null && _a !== void 0 ? _a : prev === null || prev === void 0 ? void 0 : prev.view, tick: (_b = patch.tick) !== null && _b !== void 0 ? _b : prev === null || prev === void 0 ? void 0 : prev.tick, lastAppliedTick: (_c = patch.lastAppliedTick) !== null && _c !== void 0 ? _c : prev === null || prev === void 0 ? void 0 : prev.lastAppliedTick }));
61
62
  };
63
+ this.failurePolicy = (_a = options.failurePolicy) !== null && _a !== void 0 ? _a : { decide: () => 'retry' };
64
+ this.idleBackoff = (_b = options.idleBackoff) !== null && _b !== void 0 ? _b : { factor: 2, maxMs: 30000 };
62
65
  }
63
66
  }
64
67
  exports.DeviceSyncPlan = DeviceSyncPlan;
@@ -0,0 +1,33 @@
1
+ import type * as $backend from '@lemoncloud/chatic-backend-api';
2
+ import type { SocketMessage } from '../../lib/types';
3
+ import type { DomainSyncContext, DomainSyncPlan, SyncBackoffOptions, SyncFailureInfo, SyncTargetDescriptor } from '../types';
4
+ export interface PlaceSyncTarget extends SyncTargetDescriptor {
5
+ type: 'place';
6
+ }
7
+ export interface PlaceSyncSnapshot {
8
+ id?: string;
9
+ updatedAt?: number;
10
+ view?: $backend.MySiteView;
11
+ }
12
+ export interface PlaceSyncPlanOptions {
13
+ intervalMs?: number;
14
+ idleBackoff?: SyncBackoffOptions;
15
+ resetSnapshotOnConnected?: boolean;
16
+ onUpdate?: (target: PlaceSyncTarget, view: $backend.MySiteView, previous?: PlaceSyncSnapshot) => void;
17
+ onRemove?: (target: PlaceSyncTarget, previous?: PlaceSyncSnapshot) => void;
18
+ }
19
+ /** Single-place sync: polls `place.get`, applies on `updatedAt` change, re-pulls on a `place.sync` nudge. */
20
+ export declare class PlaceSyncPlan implements DomainSyncPlan<PlaceSyncTarget> {
21
+ private readonly options;
22
+ readonly domain = "place";
23
+ readonly idleBackoff: SyncBackoffOptions;
24
+ constructor(options?: PlaceSyncPlanOptions);
25
+ supports: (target: SyncTargetDescriptor) => target is PlaceSyncTarget;
26
+ getKey: (target: PlaceSyncTarget) => string;
27
+ getIntervalMs: (target: PlaceSyncTarget) => number;
28
+ onConnected: (target: PlaceSyncTarget, ctx: DomainSyncContext) => void;
29
+ run: (target: PlaceSyncTarget, ctx: DomainSyncContext) => Promise<void>;
30
+ onTrigger: (target: PlaceSyncTarget, message: SocketMessage<any>, ctx: DomainSyncContext) => Promise<void>;
31
+ onStopped: (target: PlaceSyncTarget, _info: SyncFailureInfo, ctx: DomainSyncContext) => void;
32
+ updateLocalState: (target: PlaceSyncTarget, snapshot: unknown, ctx: DomainSyncContext) => void;
33
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PlaceSyncPlan = void 0;
13
+ /** Single-place sync: polls `place.get`, applies on `updatedAt` change, re-pulls on a `place.sync` nudge. */
14
+ class PlaceSyncPlan {
15
+ constructor(options = {}) {
16
+ var _a;
17
+ this.options = options;
18
+ this.domain = 'place';
19
+ this.supports = (target) => (target === null || target === void 0 ? void 0 : target.type) === 'place';
20
+ this.getKey = (target) => { var _a; return `place:${(_a = target === null || target === void 0 ? void 0 : target.id) !== null && _a !== void 0 ? _a : ''}`; };
21
+ this.getIntervalMs = (target) => { var _a, _b; return (_b = (_a = target.intervalMs) !== null && _a !== void 0 ? _a : this.options.intervalMs) !== null && _b !== void 0 ? _b : 2000; };
22
+ this.onConnected = (target, ctx) => {
23
+ if (this.options.resetSnapshotOnConnected === false)
24
+ return;
25
+ ctx.writeSnapshot(target, undefined);
26
+ };
27
+ this.run = (target, ctx) => __awaiter(this, void 0, void 0, function* () {
28
+ var _b, _c, _d, _e;
29
+ if (!target.id)
30
+ return;
31
+ const prev = ctx.readSnapshot(target);
32
+ const input = { id: target.id };
33
+ // place.get 응답은 backend 소유(registry response=unknown) → MySiteView 로 단언
34
+ const view = (yield ctx.client.request('place.get', input));
35
+ const nextUpdatedAt = typeof (view === null || view === void 0 ? void 0 : view.updatedAt) === 'number' ? view.updatedAt : undefined;
36
+ const prevUpdatedAt = typeof (prev === null || prev === void 0 ? void 0 : prev.updatedAt) === 'number' ? prev.updatedAt : undefined;
37
+ if (prevUpdatedAt === nextUpdatedAt && (prev === null || prev === void 0 ? void 0 : prev.view))
38
+ return;
39
+ const next = {
40
+ id: `${(_c = (_b = view === null || view === void 0 ? void 0 : view.id) !== null && _b !== void 0 ? _b : target.id) !== null && _c !== void 0 ? _c : ''}` || undefined,
41
+ updatedAt: nextUpdatedAt,
42
+ view,
43
+ };
44
+ ctx.writeSnapshot(target, next);
45
+ (_e = (_d = this.options).onUpdate) === null || _e === void 0 ? void 0 : _e.call(_d, target, view, prev);
46
+ });
47
+ this.onTrigger = (target, message, ctx) => __awaiter(this, void 0, void 0, function* () {
48
+ const data = ((message === null || message === void 0 ? void 0 : message.data) || {});
49
+ if (target.id && (data === null || data === void 0 ? void 0 : data.id) && target.id !== data.id)
50
+ return;
51
+ yield this.run(target, ctx);
52
+ });
53
+ this.onStopped = (target, _info, ctx) => {
54
+ var _a, _b;
55
+ const prev = ctx.readSnapshot(target);
56
+ (_b = (_a = this.options).onRemove) === null || _b === void 0 ? void 0 : _b.call(_a, target, prev);
57
+ };
58
+ this.updateLocalState = (target, snapshot, ctx) => {
59
+ var _a, _b;
60
+ const prev = ctx.readSnapshot(target);
61
+ const patch = (snapshot || {});
62
+ ctx.writeSnapshot(target, Object.assign(Object.assign(Object.assign({}, prev), patch), { view: (_a = patch.view) !== null && _a !== void 0 ? _a : prev === null || prev === void 0 ? void 0 : prev.view, updatedAt: (_b = patch.updatedAt) !== null && _b !== void 0 ? _b : prev === null || prev === void 0 ? void 0 : prev.updatedAt }));
63
+ };
64
+ this.idleBackoff = (_a = options.idleBackoff) !== null && _a !== void 0 ? _a : { factor: 2, maxMs: 60000 };
65
+ }
66
+ }
67
+ exports.PlaceSyncPlan = PlaceSyncPlan;
@@ -0,0 +1,33 @@
1
+ import type { ProfileView } from '@lemoncloud/chatic-socials-api';
2
+ import type { SocketMessage } from '../../lib/types';
3
+ import type { DomainSyncContext, DomainSyncPlan, SyncBackoffOptions, SyncFailureInfo, SyncTargetDescriptor } from '../types';
4
+ export interface ProfileSyncTarget extends SyncTargetDescriptor {
5
+ type: 'profile';
6
+ }
7
+ export interface ProfileSyncSnapshot {
8
+ id?: string;
9
+ updatedAt?: number;
10
+ view?: ProfileView;
11
+ }
12
+ export interface ProfileSyncPlanOptions {
13
+ intervalMs?: number;
14
+ idleBackoff?: SyncBackoffOptions;
15
+ resetSnapshotOnConnected?: boolean;
16
+ onUpdate?: (target: ProfileSyncTarget, view: ProfileView, previous?: ProfileSyncSnapshot) => void;
17
+ onRemove?: (target: ProfileSyncTarget, previous?: ProfileSyncSnapshot) => void;
18
+ }
19
+ /** Single-profile sync: polls `profile.get`, applies on `updatedAt` change, re-pulls on a `profile.sync` nudge. */
20
+ export declare class ProfileSyncPlan implements DomainSyncPlan<ProfileSyncTarget> {
21
+ private readonly options;
22
+ readonly domain = "profile";
23
+ readonly idleBackoff: SyncBackoffOptions;
24
+ constructor(options?: ProfileSyncPlanOptions);
25
+ supports: (target: SyncTargetDescriptor) => target is ProfileSyncTarget;
26
+ getKey: (target: ProfileSyncTarget) => string;
27
+ getIntervalMs: (target: ProfileSyncTarget) => number;
28
+ onConnected: (target: ProfileSyncTarget, ctx: DomainSyncContext) => void;
29
+ run: (target: ProfileSyncTarget, ctx: DomainSyncContext) => Promise<void>;
30
+ onTrigger: (target: ProfileSyncTarget, message: SocketMessage<any>, ctx: DomainSyncContext) => Promise<void>;
31
+ onStopped: (target: ProfileSyncTarget, _info: SyncFailureInfo, ctx: DomainSyncContext) => void;
32
+ updateLocalState: (target: ProfileSyncTarget, snapshot: unknown, ctx: DomainSyncContext) => void;
33
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ProfileSyncPlan = void 0;
13
+ /** Single-profile sync: polls `profile.get`, applies on `updatedAt` change, re-pulls on a `profile.sync` nudge. */
14
+ class ProfileSyncPlan {
15
+ constructor(options = {}) {
16
+ var _a;
17
+ this.options = options;
18
+ this.domain = 'profile';
19
+ this.supports = (target) => (target === null || target === void 0 ? void 0 : target.type) === 'profile';
20
+ this.getKey = (target) => { var _a; return `profile:${(_a = target === null || target === void 0 ? void 0 : target.id) !== null && _a !== void 0 ? _a : ''}`; };
21
+ this.getIntervalMs = (target) => { var _a, _b; return (_b = (_a = target.intervalMs) !== null && _a !== void 0 ? _a : this.options.intervalMs) !== null && _b !== void 0 ? _b : 2000; };
22
+ this.onConnected = (target, ctx) => {
23
+ if (this.options.resetSnapshotOnConnected === false)
24
+ return;
25
+ ctx.writeSnapshot(target, undefined);
26
+ };
27
+ this.run = (target, ctx) => __awaiter(this, void 0, void 0, function* () {
28
+ var _b, _c, _d, _e;
29
+ if (!target.id)
30
+ return;
31
+ const prev = ctx.readSnapshot(target);
32
+ const input = { id: target.id };
33
+ // profile.get 응답은 socials 소유(registry response=unknown) → ProfileView 로 단언
34
+ const view = (yield ctx.client.request('profile.get', input));
35
+ const nextUpdatedAt = typeof (view === null || view === void 0 ? void 0 : view.updatedAt) === 'number' ? view.updatedAt : undefined;
36
+ const prevUpdatedAt = typeof (prev === null || prev === void 0 ? void 0 : prev.updatedAt) === 'number' ? prev.updatedAt : undefined;
37
+ if (prevUpdatedAt === nextUpdatedAt && (prev === null || prev === void 0 ? void 0 : prev.view))
38
+ return;
39
+ const next = {
40
+ id: `${(_c = (_b = view === null || view === void 0 ? void 0 : view.id) !== null && _b !== void 0 ? _b : target.id) !== null && _c !== void 0 ? _c : ''}` || undefined,
41
+ updatedAt: nextUpdatedAt,
42
+ view,
43
+ };
44
+ ctx.writeSnapshot(target, next);
45
+ (_e = (_d = this.options).onUpdate) === null || _e === void 0 ? void 0 : _e.call(_d, target, view, prev);
46
+ });
47
+ this.onTrigger = (target, message, ctx) => __awaiter(this, void 0, void 0, function* () {
48
+ const data = ((message === null || message === void 0 ? void 0 : message.data) || {});
49
+ if (target.id && (data === null || data === void 0 ? void 0 : data.id) && target.id !== data.id)
50
+ return;
51
+ yield this.run(target, ctx);
52
+ });
53
+ this.onStopped = (target, _info, ctx) => {
54
+ var _a, _b;
55
+ const prev = ctx.readSnapshot(target);
56
+ (_b = (_a = this.options).onRemove) === null || _b === void 0 ? void 0 : _b.call(_a, target, prev);
57
+ };
58
+ this.updateLocalState = (target, snapshot, ctx) => {
59
+ var _a, _b;
60
+ const prev = ctx.readSnapshot(target);
61
+ const patch = (snapshot || {});
62
+ ctx.writeSnapshot(target, Object.assign(Object.assign(Object.assign({}, prev), patch), { view: (_a = patch.view) !== null && _a !== void 0 ? _a : prev === null || prev === void 0 ? void 0 : prev.view, updatedAt: (_b = patch.updatedAt) !== null && _b !== void 0 ? _b : prev === null || prev === void 0 ? void 0 : prev.updatedAt }));
63
+ };
64
+ this.idleBackoff = (_a = options.idleBackoff) !== null && _a !== void 0 ? _a : { factor: 2, maxMs: 60000 };
65
+ }
66
+ }
67
+ exports.ProfileSyncPlan = ProfileSyncPlan;
@@ -1,7 +1,8 @@
1
1
  import { AutoReconnectController } from './reconnect-controller';
2
2
  import { ConnectionRotationController } from './connection-rotation-controller';
3
3
  import { KeepAliveLoop } from './keep-alive-loop';
4
- import type { ClientSocketRuntime, ClientSocketV2, DomainSyncPlan, KeepAliveLoopControl, ReconnectController, SharedTimerScheduler, SyncScheduler, SyncTargetDescriptor } from './types';
4
+ import { type SyncBackoffOptions } from './sync-scheduler';
5
+ import type { ClientSocketRuntime, ClientSocketV2, DomainSyncPlan, KeepAliveLoopControl, ReconnectController, SharedTimerScheduler, SyncFailurePolicy, SyncScheduler, SyncTargetDescriptor } from './types';
5
6
  export interface ClientSocketRuntimeOptions {
6
7
  client: ClientSocketV2;
7
8
  scheduler?: SyncScheduler;
@@ -10,6 +11,9 @@ export interface ClientSocketRuntimeOptions {
10
11
  rotation?: ConnectionRotationController;
11
12
  timerScheduler?: SharedTimerScheduler;
12
13
  syncPlans?: DomainSyncPlan<any>[];
14
+ syncBackoff?: SyncBackoffOptions;
15
+ syncIdleBackoff?: SyncBackoffOptions;
16
+ failurePolicy?: SyncFailurePolicy;
13
17
  keepAliveOptions?: Omit<ConstructorParameters<typeof KeepAliveLoop>[0], 'client' | 'timerScheduler'>;
14
18
  reconnectOptions?: Omit<ConstructorParameters<typeof AutoReconnectController>[0], 'client'>;
15
19
  rotationOptions?: Omit<ConstructorParameters<typeof ConnectionRotationController>[0], 'client' | 'reconnect'>;
@@ -34,12 +34,16 @@ class SocketRuntime {
34
34
  this.stopAllSync = () => this.scheduler.stopAll();
35
35
  this.listSyncTargets = () => this.scheduler.list();
36
36
  this.updateLocalSnapshot = (target, snapshot) => this.scheduler.updateLocalSnapshot(target, snapshot);
37
- this.timerScheduler = (_b = (_a = options.timerScheduler) !== null && _a !== void 0 ? _a : options.client.timerScheduler) !== null && _b !== void 0 ? _b : new shared_timer_scheduler_1.InMemorySharedTimerScheduler();
37
+ this.timerScheduler =
38
+ (_b = (_a = options.timerScheduler) !== null && _a !== void 0 ? _a : options.client.timerScheduler) !== null && _b !== void 0 ? _b : new shared_timer_scheduler_1.InMemorySharedTimerScheduler();
38
39
  this.scheduler =
39
40
  (_c = options.scheduler) !== null && _c !== void 0 ? _c : new sync_scheduler_1.DomainSyncScheduler({
40
41
  client: options.client,
41
42
  plans: (_d = options.syncPlans) !== null && _d !== void 0 ? _d : [],
42
43
  timerScheduler: this.timerScheduler,
44
+ backoff: options.syncBackoff,
45
+ idleBackoff: options.syncIdleBackoff,
46
+ failurePolicy: options.failurePolicy,
43
47
  });
44
48
  this.keepAlive =
45
49
  (_e = options.keepAlive) !== null && _e !== void 0 ? _e : (options.keepAliveOptions