@forge/realtime 0.0.7-next.0 → 0.1.0-experimental-3f68b81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @forge/realtime
2
2
 
3
+ ## 0.1.0-experimental-3f68b81
4
+
5
+ ### Patch Changes
6
+
7
+ - fe2852a: Added backend SDK for permission handling
8
+ - 7b7e598: Rename jwt to token in signRealtimeToken output
9
+
10
+ ## 0.1.0
11
+
12
+ ### Minor Changes
13
+
14
+ - 4242274: Adds new signRealtimeToken method to the forge realtime package
15
+
16
+ ### Patch Changes
17
+
18
+ - 32da8c1: Update realtime package publish and publishGlobal to accept new token variable
19
+ - 1bf4466: Updates realtime package publish methods to use the new isGlobal field
20
+
21
+ ## 0.1.0-next.2
22
+
23
+ ### Patch Changes
24
+
25
+ - 32da8c1: Update realtime package publish and publishGlobal to accept new token variable
26
+
27
+ ## 0.1.0-next.1
28
+
29
+ ### Minor Changes
30
+
31
+ - 4242274: Adds new signRealtimeToken method to the forge realtime package
32
+
3
33
  ## 0.0.7-next.0
4
34
 
5
35
  ### Patch Changes
@@ -4,6 +4,33 @@ const tslib_1 = require("tslib");
4
4
  const runtime = tslib_1.__importStar(require("../runtime"));
5
5
  const publish_1 = require("../publish");
6
6
  const utils_1 = require("./utils");
7
+ const TEST_EVENT_ID = 'event-id';
8
+ const TEST_EVENT_TIMESTAMP = '1234567890';
9
+ const MOCK_CHANNEL = 'my-channel';
10
+ const MOCK_PUBLISH_OPTIONS = { token: 'my-token' };
11
+ const MOCK_EVENT_PAYLOAD_STRING = 'this is an event payload';
12
+ const MOCK_EVENT_PAYLOAD_OBJECT = { value: 'this is a test payload', funLevel: 100 };
13
+ const MOCK_FETCH_RESPONSE = {
14
+ errors: [],
15
+ data: {
16
+ ecosystem: {
17
+ publishRealtimeChannel: {
18
+ eventId: TEST_EVENT_ID,
19
+ eventTimestamp: TEST_EVENT_TIMESTAMP
20
+ },
21
+ success: true
22
+ }
23
+ }
24
+ };
25
+ const MOCK_ERRORS = [
26
+ {
27
+ message: 'Error message',
28
+ extensions: {
29
+ errorType: 'Error type',
30
+ statusCode: 500
31
+ }
32
+ }
33
+ ];
7
34
  describe('publish', () => {
8
35
  beforeEach(() => {
9
36
  jest.restoreAllMocks();
@@ -12,26 +39,25 @@ describe('publish', () => {
12
39
  it('should publish an event with FCT in header', async () => {
13
40
  jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
14
41
  const mockForgeFetch = jest.fn().mockResolvedValue({
15
- text: jest.fn().mockResolvedValue('Hello Magic'),
42
+ json: jest.fn().mockResolvedValue(MOCK_FETCH_RESPONSE),
16
43
  status: 200,
17
44
  headers: { get: jest.fn().mockReturnValue(undefined) }
18
45
  });
19
46
  global.__forge_fetch__ = mockForgeFetch;
20
- const response = await (0, publish_1.publish)('my-channel', 'this is an event payload');
21
- expect(response.status).toBe(200);
47
+ const response = await (0, publish_1.publish)(MOCK_CHANNEL, MOCK_EVENT_PAYLOAD_STRING, MOCK_PUBLISH_OPTIONS);
48
+ expect(response).toEqual({ eventId: TEST_EVENT_ID, eventTimestamp: TEST_EVENT_TIMESTAMP });
22
49
  expect(mockForgeFetch.mock.calls).toMatchSnapshot();
23
50
  });
24
51
  it('should publish an event when the payload is an object', async () => {
25
52
  jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
26
53
  const mockForgeFetch = jest.fn().mockResolvedValue({
27
- text: jest.fn().mockResolvedValue('Hello Magic'),
54
+ json: jest.fn().mockResolvedValue(MOCK_FETCH_RESPONSE),
28
55
  status: 200,
29
56
  headers: { get: jest.fn().mockReturnValue(undefined) }
30
57
  });
31
58
  global.__forge_fetch__ = mockForgeFetch;
32
- const payload = { value: 'this is a test payload', funLevel: 100 };
33
- const response = await (0, publish_1.publish)('my-channel', JSON.stringify(payload));
34
- expect(response.status).toBe(200);
59
+ const response = await (0, publish_1.publish)(MOCK_CHANNEL, JSON.stringify(MOCK_EVENT_PAYLOAD_OBJECT), MOCK_PUBLISH_OPTIONS);
60
+ expect(response).toEqual({ eventId: TEST_EVENT_ID, eventTimestamp: TEST_EVENT_TIMESTAMP });
35
61
  expect(mockForgeFetch.mock.calls).toMatchSnapshot();
36
62
  });
37
63
  it('should throw an error when Forge Outbound Proxy returns an error', async () => {
@@ -42,11 +68,30 @@ describe('publish', () => {
42
68
  headers: { get: jest.fn().mockReturnValue('UPSTREAM_FAILURE') }
43
69
  });
44
70
  global.__forge_fetch__ = mockForgeFetch;
45
- const payload = { value: 'this is a test payload', funLevel: 100 };
46
- const response = (0, publish_1.publish)('my-channel', JSON.stringify(payload));
71
+ const response = (0, publish_1.publish)(MOCK_CHANNEL, JSON.stringify(MOCK_EVENT_PAYLOAD_OBJECT), MOCK_PUBLISH_OPTIONS);
47
72
  await expect(response).rejects.toThrow('Forge platform failed to process runtime HTTP request - 502 - UPSTREAM_FAILURE');
48
73
  expect(mockForgeFetch.mock.calls).toMatchSnapshot();
49
74
  });
75
+ it('should return null eventId and eventTimestamp when the response has errors', async () => {
76
+ jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
77
+ const mockForgeFetch = jest.fn().mockResolvedValue({
78
+ json: jest.fn().mockResolvedValue({
79
+ errors: MOCK_ERRORS,
80
+ data: {
81
+ ecosystem: {
82
+ publishRealtimeChannel: null,
83
+ }
84
+ }
85
+ }),
86
+ status: 200,
87
+ headers: { get: jest.fn().mockReturnValue(undefined) }
88
+ });
89
+ global.__forge_fetch__ = mockForgeFetch;
90
+ const payload = { value: 'this is a test payload', funLevel: 100 };
91
+ const response = await (0, publish_1.publish)('my-channel', JSON.stringify(payload));
92
+ expect(response).toEqual({ eventId: null, eventTimestamp: null, errors: MOCK_ERRORS });
93
+ expect(mockForgeFetch.mock.calls).toMatchSnapshot();
94
+ });
50
95
  });
51
96
  describe('publishGlobal', () => {
52
97
  beforeEach(() => {
@@ -56,26 +101,25 @@ describe('publishGlobal', () => {
56
101
  it('should publish an event', async () => {
57
102
  jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
58
103
  const mockForgeFetch = jest.fn().mockResolvedValue({
59
- text: jest.fn().mockResolvedValue('Hello Magic'),
104
+ json: jest.fn().mockResolvedValue(MOCK_FETCH_RESPONSE),
60
105
  status: 200,
61
106
  headers: { get: jest.fn().mockReturnValue(undefined) }
62
107
  });
63
108
  global.__forge_fetch__ = mockForgeFetch;
64
- const response = await (0, publish_1.publishGlobal)('my-channel', 'this is an event payload');
65
- expect(response.status).toBe(200);
109
+ const response = await (0, publish_1.publishGlobal)(MOCK_CHANNEL, MOCK_EVENT_PAYLOAD_STRING, MOCK_PUBLISH_OPTIONS);
110
+ expect(response).toEqual({ eventId: TEST_EVENT_ID, eventTimestamp: TEST_EVENT_TIMESTAMP });
66
111
  expect(mockForgeFetch.mock.calls).toMatchSnapshot();
67
112
  });
68
113
  it('should publish an event when the payload is an object', async () => {
69
114
  jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
70
115
  const mockForgeFetch = jest.fn().mockResolvedValue({
71
- text: jest.fn().mockResolvedValue('Hello Magic'),
116
+ json: jest.fn().mockResolvedValue(MOCK_FETCH_RESPONSE),
72
117
  status: 200,
73
118
  headers: { get: jest.fn().mockReturnValue(undefined) }
74
119
  });
75
120
  global.__forge_fetch__ = mockForgeFetch;
76
- const payload = { value: 'this is a test payload', funLevel: 100 };
77
- const response = await (0, publish_1.publishGlobal)('my-channel', JSON.stringify(payload));
78
- expect(response.status).toBe(200);
121
+ const response = await (0, publish_1.publishGlobal)(MOCK_CHANNEL, JSON.stringify(MOCK_EVENT_PAYLOAD_OBJECT), MOCK_PUBLISH_OPTIONS);
122
+ expect(response).toEqual({ eventId: TEST_EVENT_ID, eventTimestamp: TEST_EVENT_TIMESTAMP });
79
123
  expect(mockForgeFetch.mock.calls).toMatchSnapshot();
80
124
  });
81
125
  it('should throw an error when Forge Outbound Proxy returns an error', async () => {
@@ -86,9 +130,28 @@ describe('publishGlobal', () => {
86
130
  headers: { get: jest.fn().mockReturnValue('UPSTREAM_FAILURE') }
87
131
  });
88
132
  global.__forge_fetch__ = mockForgeFetch;
89
- const payload = { value: 'this is a test payload', funLevel: 100 };
90
- const response = (0, publish_1.publishGlobal)('my-channel', JSON.stringify(payload));
133
+ const response = (0, publish_1.publishGlobal)(MOCK_CHANNEL, JSON.stringify(MOCK_EVENT_PAYLOAD_OBJECT), MOCK_PUBLISH_OPTIONS);
91
134
  await expect(response).rejects.toThrow('Forge platform failed to process runtime HTTP request - 502 - UPSTREAM_FAILURE');
92
135
  expect(mockForgeFetch.mock.calls).toMatchSnapshot();
93
136
  });
137
+ it('should return null eventId and eventTimestamp when the response has errors', async () => {
138
+ jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
139
+ const mockForgeFetch = jest.fn().mockResolvedValue({
140
+ json: jest.fn().mockResolvedValue({
141
+ errors: MOCK_ERRORS,
142
+ data: {
143
+ ecosystem: {
144
+ publishRealtimeChannel: null,
145
+ }
146
+ }
147
+ }),
148
+ status: 200,
149
+ headers: { get: jest.fn().mockReturnValue(undefined) }
150
+ });
151
+ global.__forge_fetch__ = mockForgeFetch;
152
+ const payload = { value: 'this is a test payload', funLevel: 100 };
153
+ const response = await (0, publish_1.publishGlobal)('my-channel', JSON.stringify(payload));
154
+ expect(response).toEqual({ eventId: null, eventTimestamp: null, errors: MOCK_ERRORS });
155
+ expect(mockForgeFetch.mock.calls).toMatchSnapshot();
156
+ });
94
157
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=signRealtimeToken.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signRealtimeToken.test.d.ts","sourceRoot":"","sources":["../../src/__test__/signRealtimeToken.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const runtime = tslib_1.__importStar(require("../runtime"));
5
+ const signRealtimeToken_1 = require("../signRealtimeToken");
6
+ const utils_1 = require("./utils");
7
+ const TEST_TOKEN = 'jwt-token';
8
+ const TEST_EXPIRES_AT = '1234567890';
9
+ const MOCK_ERRORS = [
10
+ {
11
+ message: 'Error message',
12
+ extensions: {
13
+ errorType: 'Error type',
14
+ statusCode: 500
15
+ }
16
+ }
17
+ ];
18
+ describe('signRealtimeToken', () => {
19
+ beforeEach(() => {
20
+ jest.restoreAllMocks();
21
+ jest.resetAllMocks();
22
+ });
23
+ it('should send a signRealtimeToken request and recieve a jwt and expiresAt', async () => {
24
+ jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
25
+ const mockForgeFetch = jest.fn().mockResolvedValue({
26
+ json: jest.fn().mockResolvedValue({
27
+ errors: [],
28
+ data: {
29
+ ecosystem: {
30
+ signRealtimeToken: {
31
+ errors: [],
32
+ forgeRealtimeToken: {
33
+ jwt: TEST_TOKEN,
34
+ expiresAt: TEST_EXPIRES_AT
35
+ },
36
+ success: true
37
+ }
38
+ }
39
+ }
40
+ }),
41
+ status: 200,
42
+ headers: { get: jest.fn().mockReturnValue(undefined) }
43
+ });
44
+ global.__forge_fetch__ = mockForgeFetch;
45
+ const response = await (0, signRealtimeToken_1.signRealtimeToken)('my-channel', { 'test-key': 'test-value' });
46
+ expect(response).toEqual({ token: TEST_TOKEN, expiresAt: TEST_EXPIRES_AT });
47
+ expect(mockForgeFetch.mock.calls).toMatchSnapshot();
48
+ });
49
+ it('should throw an error when Forge Outbound Proxy returns an error', async () => {
50
+ jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
51
+ const mockForgeFetch = jest.fn().mockResolvedValue({
52
+ text: jest.fn().mockResolvedValue('Error occurred'),
53
+ status: 502,
54
+ headers: { get: jest.fn().mockReturnValue('UPSTREAM_FAILURE') }
55
+ });
56
+ global.__forge_fetch__ = mockForgeFetch;
57
+ const response = (0, signRealtimeToken_1.signRealtimeToken)('my-channel', { 'test-key': 'test-value' });
58
+ await expect(response).rejects.toThrow('Forge platform failed to process runtime HTTP request - 502 - UPSTREAM_FAILURE');
59
+ expect(mockForgeFetch.mock.calls).toMatchSnapshot();
60
+ });
61
+ it('should send generic error when errors are returned', async () => {
62
+ jest.spyOn(runtime, '__getRuntime').mockReturnValue(utils_1.FORGE_RUNTIME);
63
+ const mockForgeFetch = jest.fn().mockResolvedValue({
64
+ json: jest.fn().mockResolvedValue({
65
+ errors: MOCK_ERRORS,
66
+ data: {
67
+ ecosystem: {
68
+ signRealtimeToken: null,
69
+ }
70
+ }
71
+ }),
72
+ status: 200,
73
+ headers: { get: jest.fn().mockReturnValue(undefined) }
74
+ });
75
+ global.__forge_fetch__ = mockForgeFetch;
76
+ const response = await (0, signRealtimeToken_1.signRealtimeToken)('my-channel', { 'test-key': 'test-value' });
77
+ expect(response).toEqual({ token: null, expiresAt: null, errors: MOCK_ERRORS });
78
+ expect(mockForgeFetch.mock.calls).toMatchSnapshot();
79
+ });
80
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/__test__/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,eAAO,MAAM,aAAa,EAAE,YA6B3B,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/__test__/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,eAAO,MAAM,aAAa,EAAE,YAkD3B,CAAC"}
@@ -16,7 +16,28 @@ exports.FORGE_RUNTIME = {
16
16
  functionKey: 'functionKey',
17
17
  moduleType: 'moduleType',
18
18
  moduleKey: 'moduleKey',
19
- license: { isActive: true }
19
+ license: { isActive: true },
20
+ permissions: {
21
+ scopes: ['read:confluence-content.summary', 'write:confluence-content'],
22
+ content: {
23
+ scripts: [],
24
+ styles: []
25
+ },
26
+ external: {
27
+ fetch: {
28
+ backend: [],
29
+ client: []
30
+ },
31
+ fonts: [],
32
+ frames: [],
33
+ navigation: [],
34
+ images: [],
35
+ media: [],
36
+ scripts: [],
37
+ styles: []
38
+ },
39
+ format: () => ''
40
+ }
20
41
  },
21
42
  tracing: {
22
43
  traceId: 'traceId',
package/out/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { publish, publishGlobal } from './publish';
2
+ export { signRealtimeToken } from './signRealtimeToken';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
package/out/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.publishGlobal = exports.publish = void 0;
3
+ exports.signRealtimeToken = exports.publishGlobal = exports.publish = void 0;
4
4
  var publish_1 = require("./publish");
5
5
  Object.defineProperty(exports, "publish", { enumerable: true, get: function () { return publish_1.publish; } });
6
6
  Object.defineProperty(exports, "publishGlobal", { enumerable: true, get: function () { return publish_1.publishGlobal; } });
7
+ var signRealtimeToken_1 = require("./signRealtimeToken");
8
+ Object.defineProperty(exports, "signRealtimeToken", { enumerable: true, get: function () { return signRealtimeToken_1.signRealtimeToken; } });
package/out/publish.d.ts CHANGED
@@ -1,5 +1,23 @@
1
- export declare const getForgeProxyError: (response: Response) => string | null;
2
- export declare const handleProxyResponseErrors: (response: Response) => void;
3
- export declare const publish: (channelName: string, eventPayload: string) => Promise<any>;
4
- export declare const publishGlobal: (channelName: string, eventPayload: string) => Promise<any>;
1
+ interface PublishOptions {
2
+ token?: string;
3
+ }
4
+ export declare const publish: (channelName: string, eventPayload: string, options?: PublishOptions) => Promise<{
5
+ eventId: null;
6
+ eventTimestamp: null;
7
+ errors: any;
8
+ } | {
9
+ eventId: any;
10
+ eventTimestamp: any;
11
+ errors?: undefined;
12
+ }>;
13
+ export declare const publishGlobal: (channelName: string, eventPayload: string, options?: PublishOptions) => Promise<{
14
+ eventId: null;
15
+ eventTimestamp: null;
16
+ errors: any;
17
+ } | {
18
+ eventId: any;
19
+ eventTimestamp: any;
20
+ errors?: undefined;
21
+ }>;
22
+ export {};
5
23
  //# sourceMappingURL=publish.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB,aAAc,QAAQ,kBAA8C,CAAC;AACpG,eAAO,MAAM,yBAAyB,aAAc,QAAQ,KAAG,IAK9D,CAAC;AAwBF,eAAO,MAAM,OAAO,gBAAuB,MAAM,gBAAgB,MAAM,iBAkCtE,CAAC;AAEF,eAAO,MAAM,aAAa,gBAAuB,MAAM,gBAAgB,MAAM,iBAiC5E,CAAC"}
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"AAwBA,UAAU,cAAc;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,OAAO,gBAAuB,MAAM,gBAAgB,MAAM,YAAY,cAAc;;;;;;;;EAiEhG,CAAC;AAEF,eAAO,MAAM,aAAa,gBAAuB,MAAM,gBAAgB,MAAM,YAAY,cAAc;;;;;;;;EAgEtG,CAAC"}
package/out/publish.js CHANGED
@@ -1,22 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.publishGlobal = exports.publish = exports.handleProxyResponseErrors = exports.getForgeProxyError = void 0;
4
- const api_1 = require("@forge/api");
3
+ exports.publishGlobal = exports.publish = void 0;
5
4
  const runtime_1 = require("./runtime");
6
- const getForgeProxyError = (response) => response.headers.get('forge-proxy-error');
7
- exports.getForgeProxyError = getForgeProxyError;
8
- const handleProxyResponseErrors = (response) => {
9
- const errorReason = (0, exports.getForgeProxyError)(response);
10
- if (errorReason) {
11
- throw new api_1.ProxyRequestError(response.status, errorReason);
12
- }
13
- };
14
- exports.handleProxyResponseErrors = handleProxyResponseErrors;
5
+ const utils_1 = require("./utils");
15
6
  const graphqlBody = `mutation publishRealtimeChannel(
16
7
  $installationId: ID!
17
8
  $name: String!
18
9
  $payload: String!
19
10
  $isGlobal: Boolean
11
+ $token: String
20
12
  ){
21
13
  ecosystem {
22
14
  publishRealtimeChannel(
@@ -24,17 +16,17 @@ const graphqlBody = `mutation publishRealtimeChannel(
24
16
  name: $name
25
17
  payload: $payload
26
18
  isGlobal: $isGlobal
19
+ token: $token
27
20
  ) {
28
21
  eventId,
29
22
  eventTimestamp
30
23
  }
31
24
  }
32
25
  }`;
33
- const prodEnvErrorMessage = 'Forge realtime usage is restricted to Forge apps in a non-production environment. Please see https://developer.atlassian.com/platform/forge/cli-reference/environments/ for reference on Forge app environments.';
34
- const publish = async (channelName, eventPayload) => {
26
+ const publish = async (channelName, eventPayload, options) => {
35
27
  const { appContext, realtime } = (0, runtime_1.__getRuntime)();
36
28
  if (appContext?.environmentType === 'PRODUCTION') {
37
- throw new Error(prodEnvErrorMessage);
29
+ throw new Error(utils_1.prodEnvErrorMessage);
38
30
  }
39
31
  const response = await global.__forge_fetch__({
40
32
  type: 'realtime'
@@ -46,22 +38,48 @@ const publish = async (channelName, eventPayload) => {
46
38
  installationId: appContext.installationId,
47
39
  name: channelName,
48
40
  payload: eventPayload,
49
- isGlobal: false
41
+ isGlobal: false,
42
+ token: options?.token
50
43
  }
51
44
  }),
45
+ errors: [],
52
46
  headers: {
53
47
  'Content-Type': 'application/json',
54
48
  'x-forge-context-token': realtime?.contextToken
55
49
  }
56
50
  });
57
- (0, exports.handleProxyResponseErrors)(response);
58
- return response;
51
+ (0, utils_1.handleProxyResponseErrors)(response);
52
+ const awaitedResponse = await response.json();
53
+ const { data, errors } = awaitedResponse;
54
+ if (errors && errors.length > 0) {
55
+ return {
56
+ eventId: null,
57
+ eventTimestamp: null,
58
+ errors
59
+ };
60
+ }
61
+ if (!data) {
62
+ return {
63
+ eventId: null,
64
+ eventTimestamp: null,
65
+ errors: [
66
+ {
67
+ message: 'Error publishing event to channel.'
68
+ }
69
+ ]
70
+ };
71
+ }
72
+ const { eventId, eventTimestamp } = awaitedResponse.data.ecosystem.publishRealtimeChannel;
73
+ return {
74
+ eventId,
75
+ eventTimestamp
76
+ };
59
77
  };
60
78
  exports.publish = publish;
61
- const publishGlobal = async (channelName, eventPayload) => {
79
+ const publishGlobal = async (channelName, eventPayload, options) => {
62
80
  const { appContext } = (0, runtime_1.__getRuntime)();
63
81
  if (appContext?.environmentType === 'PRODUCTION') {
64
- throw new Error(prodEnvErrorMessage);
82
+ throw new Error(utils_1.prodEnvErrorMessage);
65
83
  }
66
84
  const response = await global.__forge_fetch__({
67
85
  type: 'realtime'
@@ -73,14 +91,40 @@ const publishGlobal = async (channelName, eventPayload) => {
73
91
  installationId: appContext.installationId,
74
92
  name: channelName,
75
93
  payload: eventPayload,
76
- isGlobal: true
94
+ isGlobal: true,
95
+ token: options?.token
77
96
  }
78
97
  }),
98
+ errors: [],
79
99
  headers: {
80
100
  'Content-Type': 'application/json'
81
101
  }
82
102
  });
83
- (0, exports.handleProxyResponseErrors)(response);
84
- return response;
103
+ (0, utils_1.handleProxyResponseErrors)(response);
104
+ const awaitedResponse = await response.json();
105
+ const { data, errors } = awaitedResponse;
106
+ if (errors && errors.length > 0) {
107
+ return {
108
+ eventId: null,
109
+ eventTimestamp: null,
110
+ errors
111
+ };
112
+ }
113
+ if (!data) {
114
+ return {
115
+ eventId: null,
116
+ eventTimestamp: null,
117
+ errors: [
118
+ {
119
+ message: 'Error publishing global event to channel.'
120
+ }
121
+ ]
122
+ };
123
+ }
124
+ const { eventId, eventTimestamp } = data.ecosystem.publishRealtimeChannel;
125
+ return {
126
+ eventId,
127
+ eventTimestamp
128
+ };
85
129
  };
86
130
  exports.publishGlobal = publishGlobal;
@@ -0,0 +1,10 @@
1
+ export declare const signRealtimeToken: (channelName: string, claims: any) => Promise<{
2
+ token: null;
3
+ expiresAt: null;
4
+ errors: any;
5
+ } | {
6
+ token: any;
7
+ expiresAt: any;
8
+ errors?: undefined;
9
+ }>;
10
+ //# sourceMappingURL=signRealtimeToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signRealtimeToken.d.ts","sourceRoot":"","sources":["../src/signRealtimeToken.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,iBAAiB,gBAAuB,MAAM,UAAU,GAAG;;;;;;;;EA6DvE,CAAC"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signRealtimeToken = void 0;
4
+ const runtime_1 = require("./runtime");
5
+ const utils_1 = require("./utils");
6
+ const graphqlBody = `mutation signRealtimeToken(
7
+ $channelName: String!
8
+ $claims: JSON!
9
+ ){
10
+ ecosystem {
11
+ signRealtimeToken(
12
+ channelName: $channelName
13
+ claims: $claims
14
+ ) {
15
+ errors {
16
+ message
17
+ extensions {
18
+ errorType
19
+ statusCode
20
+ }
21
+ }
22
+ forgeRealtimeToken {
23
+ jwt
24
+ expiresAt
25
+ }
26
+ success
27
+ }
28
+ }
29
+ }`;
30
+ const signRealtimeToken = async (channelName, claims) => {
31
+ const { appContext } = (0, runtime_1.__getRuntime)();
32
+ if (appContext?.environmentType === 'PRODUCTION') {
33
+ throw new Error(utils_1.prodEnvErrorMessage);
34
+ }
35
+ const response = await global.__forge_fetch__({
36
+ type: 'realtime'
37
+ }, '/', {
38
+ method: 'POST',
39
+ body: JSON.stringify({
40
+ query: graphqlBody,
41
+ variables: {
42
+ channelName,
43
+ claims
44
+ }
45
+ }),
46
+ errors: [],
47
+ headers: {
48
+ 'Content-Type': 'application/json'
49
+ }
50
+ });
51
+ (0, utils_1.handleProxyResponseErrors)(response);
52
+ const awaitedResponse = await response.json();
53
+ const { data, errors } = awaitedResponse;
54
+ if (errors && errors.length > 0) {
55
+ return {
56
+ token: null,
57
+ expiresAt: null,
58
+ errors
59
+ };
60
+ }
61
+ if (!data) {
62
+ return {
63
+ token: null,
64
+ expiresAt: null,
65
+ errors: [
66
+ {
67
+ message: 'Error signing realtime token.'
68
+ }
69
+ ]
70
+ };
71
+ }
72
+ const { jwt, expiresAt } = data.ecosystem.signRealtimeToken.forgeRealtimeToken;
73
+ return {
74
+ token: jwt,
75
+ expiresAt
76
+ };
77
+ };
78
+ exports.signRealtimeToken = signRealtimeToken;
package/out/utils.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare const handleProxyResponseErrors: (response: Response) => void;
2
+ export declare const prodEnvErrorMessage = "Forge realtime usage is restricted to Forge apps in a non-production environment. Please see https://developer.atlassian.com/platform/forge/cli-reference/environments/ for reference on Forge app environments.";
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,yBAAyB,aAAc,QAAQ,KAAG,IAK9D,CAAC;AAEF,eAAO,MAAM,mBAAmB,qNACoL,CAAC"}
package/out/utils.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prodEnvErrorMessage = exports.handleProxyResponseErrors = void 0;
4
+ const api_1 = require("@forge/api");
5
+ const getForgeProxyError = (response) => response.headers.get('forge-proxy-error');
6
+ const handleProxyResponseErrors = (response) => {
7
+ const errorReason = getForgeProxyError(response);
8
+ if (errorReason) {
9
+ throw new api_1.ProxyRequestError(response.status, errorReason);
10
+ }
11
+ };
12
+ exports.handleProxyResponseErrors = handleProxyResponseErrors;
13
+ exports.prodEnvErrorMessage = 'Forge realtime usage is restricted to Forge apps in a non-production environment. Please see https://developer.atlassian.com/platform/forge/cli-reference/environments/ for reference on Forge app environments.';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/realtime",
3
- "version": "0.0.7-next.0",
3
+ "version": "0.1.0-experimental-3f68b81",
4
4
  "description": "Forge realtime",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@atlassian/metrics-interface": "4.0.0",
17
- "@forge/api": "6.1.0",
17
+ "@forge/api": "6.1.1-experimental-3f68b81",
18
18
  "@types/node": "20.19.1"
19
19
  },
20
20
  "publishConfig": {