@jumoo/processing 18.3.0 → 19.0.0-beta01

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.
@@ -39,7 +39,9 @@ let JumooProcessingErrorModal = class JumooProcessingErrorModal extends UmbModal
39
39
  <div class="detail">${this.data?.error.action}</div>
40
40
  </div>
41
41
  <div class="more">
42
- <div class="label"></div>
42
+ <div class="label">
43
+ <umb-localize key="jumooProcError_step"></umb-localize>
44
+ </div>
43
45
  <div class="detail">
44
46
  ${this.data?.error.step}
45
47
  ${when(this.data?.error.subStep, () => html `(${this.data?.error.subStep})`)}
@@ -9,10 +9,13 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _PipelineContext_instances, _PipelineContext_repository, _PipelineContext_hubContext, _PipelineContext_pipeline, _PipelineContext_state, _PipelineContext_options, _PipelineContext_results, _PipelineContext_message, _PipelineContext_backgroundState, _PipelineContext_conflict, _PipelineContext_clientId, _PipelineContext_hasConnectedBefore, _PipelineContext_onClientIdChanged, _PipelineContext_isInFlight, _PipelineContext_setConflict, _PipelineContext_trigger, _PipelineContext_close, _PipelineContext_finalized, _PipelineContext_valid, _PipelineContext_pending;
12
+ var _PipelineContext_instances, _PipelineContext_repository, _PipelineContext_hubContext, _PipelineContext_pipeline, _PipelineContext_state, _PipelineContext_options, _PipelineContext_results, _PipelineContext_message, _PipelineContext_backgroundState, _PipelineContext_conflict, _PipelineContext_clientId, _PipelineContext_hasConnectedBefore, _PipelineContext_onClientIdChanged, _PipelineContext_isInFlight, _PipelineContext_setConflict, _PipelineContext_retryAfterSignIn, _PipelineContext_waitForSignIn, _PipelineContext_trigger, _PipelineContext_close, _PipelineContext_finalized, _PipelineContext_valid, _PipelineContext_pending;
13
13
  import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
14
14
  import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
15
15
  import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
16
+ import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
17
+ import { UmbApiError } from '@umbraco-cms/backoffice/resources';
18
+ import { filter, firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
16
19
  import { PipelineStatus, PipelineRepository, ProcessingSignalRContext, getPipelineConflict, } from '@jumoo/processing';
17
20
  /**
18
21
  * @description context for keeping pipeline status and progress as we do things.
@@ -65,8 +68,7 @@ export class PipelineContext extends UmbControllerBase {
65
68
  __classPrivateFieldSet(this, _PipelineContext_repository, new PipelineRepository(this), "f");
66
69
  this.provideContext(JUMOO_PROCESSING_CONTEXT, this);
67
70
  __classPrivateFieldSet(this, _PipelineContext_hubContext, new ProcessingSignalRContext(this), "f");
68
- const token = '[redacted]';
69
- __classPrivateFieldGet(this, _PipelineContext_hubContext, "f").startHub(token, ['message', 'state']);
71
+ __classPrivateFieldGet(this, _PipelineContext_hubContext, "f").startHub(['message', 'state']);
70
72
  this.observe(__classPrivateFieldGet(this, _PipelineContext_hubContext, "f").messagesOf('message'), (message) => {
71
73
  if (!message?.message)
72
74
  return;
@@ -93,7 +95,7 @@ export class PipelineContext extends UmbControllerBase {
93
95
  }
94
96
  async createPipeline(processor, strategy, options) {
95
97
  __classPrivateFieldGet(this, _PipelineContext_conflict, "f").setValue(undefined);
96
- const { data, error } = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryCreate(processor, strategy, options);
98
+ const { data, error } = await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_retryAfterSignIn).call(this, () => __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryCreate(processor, strategy, options));
97
99
  if (error) {
98
100
  await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_setConflict).call(this, error);
99
101
  return;
@@ -123,8 +125,10 @@ export class PipelineContext extends UmbControllerBase {
123
125
  async process() {
124
126
  if (!__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value)
125
127
  return;
126
- const clientId = __classPrivateFieldGet(this, _PipelineContext_hubContext, "f")?.getClientId() ?? null;
127
- const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryProcess(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, clientId);
128
+ const pipelineId = __classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value;
129
+ const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_retryAfterSignIn).call(this, () =>
130
+ // the connection id is read each time: the hub can reconnect while the user signs in.
131
+ __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryProcess(pipelineId, __classPrivateFieldGet(this, _PipelineContext_hubContext, "f")?.getClientId() ?? null));
128
132
  if (error) {
129
133
  await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_setConflict).call(this, error);
130
134
  return undefined;
@@ -143,7 +147,8 @@ export class PipelineContext extends UmbControllerBase {
143
147
  async updateOptions(options) {
144
148
  if (!__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value)
145
149
  return;
146
- const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryUpdate(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, options);
150
+ const pipelineId = __classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value;
151
+ const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_retryAfterSignIn).call(this, () => __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryUpdate(pipelineId, options));
147
152
  if (error)
148
153
  await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_setConflict).call(this, error);
149
154
  if (response?.options) {
@@ -157,7 +162,8 @@ export class PipelineContext extends UmbControllerBase {
157
162
  async saveOptions() {
158
163
  if (!__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value)
159
164
  return;
160
- const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryUpdate(__classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value, __classPrivateFieldGet(this, _PipelineContext_options, "f").value);
165
+ const pipelineId = __classPrivateFieldGet(this, _PipelineContext_pipeline, "f").value;
166
+ const { data: response, error } = await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_retryAfterSignIn).call(this, () => __classPrivateFieldGet(this, _PipelineContext_repository, "f").tryUpdate(pipelineId, __classPrivateFieldGet(this, _PipelineContext_options, "f").value));
161
167
  if (error)
162
168
  await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_setConflict).call(this, error);
163
169
  if (response?.options) {
@@ -290,6 +296,48 @@ async function _PipelineContext_setConflict(error) {
290
296
  if (holder)
291
297
  __classPrivateFieldGet(this, _PipelineContext_conflict, "f").setValue({ ...conflict, holder });
292
298
  return true;
299
+ }, _PipelineContext_retryAfterSignIn =
300
+ /**
301
+ * @description makes a create, update or process call, and makes it again if it hit a session
302
+ * timeout. From Umbraco 19 a 401 on a POST opens the login modal but, unlike a GET, the
303
+ * request is not retried - so a run would drop back to waiting mid-pipeline, or carry on with
304
+ * options that were never saved. The 401 comes from the auth middleware, before our
305
+ * controller runs, so once the user has signed back in it is safe to send the same call again.
306
+ * @param call the request, made again on each attempt.
307
+ * @returns the first result that isn't a 401.
308
+ */
309
+ async function _PipelineContext_retryAfterSignIn(call) {
310
+ let result = await call();
311
+ while (result.error && (await __classPrivateFieldGet(this, _PipelineContext_instances, "m", _PipelineContext_waitForSignIn).call(this, result.error))) {
312
+ result = await call();
313
+ }
314
+ return result;
315
+ }, _PipelineContext_waitForSignIn =
316
+ /**
317
+ * @description waits for the user to sign back in after a session timeout.
318
+ * @param error the error from a request.
319
+ * @returns true once the user has signed back in after a 401, false for any other error.
320
+ */
321
+ async function _PipelineContext_waitForSignIn(error) {
322
+ // the generated client doesn't throw, so this is usually the problem details themselves
323
+ // rather than an UmbApiError - check both, as getPipelineConflict does.
324
+ const status = UmbApiError.isUmbApiError(error)
325
+ ? error.status
326
+ : error?.status;
327
+ if (status !== 401)
328
+ return false;
329
+ const authContext = await this.getContext(UMB_AUTH_CONTEXT);
330
+ if (!authContext)
331
+ return false;
332
+ // the 401 clears the session, but the request can land before that does - so wait for
333
+ // the session to be gone and then back, rather than taking a still-signed-in value.
334
+ let signedOut = false;
335
+ await firstValueFrom(authContext.isAuthorized.pipe(filter((isAuthorized) => {
336
+ if (!isAuthorized)
337
+ signedOut = true;
338
+ return isAuthorized && signedOut;
339
+ })));
340
+ return true;
293
341
  };
294
342
  export default PipelineContext;
295
343
  export const JUMOO_PROCESSING_CONTEXT = new UmbContextToken('JumooPipelineContext');
@@ -9,6 +9,15 @@ export declare class ProcessingSignalRContext extends UmbControllerBase {
9
9
  readonly message: import("rxjs").Observable<ProcessingHubMessage[]>;
10
10
  readonly clientId: import("rxjs").Observable<string | null>;
11
11
  readonly connectionState: import("rxjs").Observable<ProcessingConnectionState>;
12
+ /**
13
+ * @description connects to the processing hub. From Umbraco 19 the backoffice session is a
14
+ * cookie, so the connection sends that rather than a token.
15
+ */
16
+ startHub(methods: string[]): void;
17
+ /**
18
+ * @deprecated there is no client-side token from Umbraco 19 - call `startHub(methods)`. The
19
+ * token is ignored. Will be removed in v21.
20
+ */
12
21
  startHub(token: string, methods: string[]): void;
13
22
  hostConnected(): void;
14
23
  hostDisconnected(): void;
@@ -28,8 +28,9 @@ export class ProcessingSignalRContext extends UmbControllerBase {
28
28
  this.connectionState = __classPrivateFieldGet(this, _ProcessingSignalRContext_connectionState, "f").asObservable();
29
29
  this.provideContext(JUMOO_PROCESSING_SIGNALR_CONTEXT, this);
30
30
  }
31
- startHub(token, methods) {
32
- __classPrivateFieldGet(this, _ProcessingSignalRContext_instances, "m", _ProcessingSignalRContext_setupConnection).call(this, JUMOO_PROCESSING_HUB_PATH, token, methods);
31
+ startHub(tokenOrMethods, methods) {
32
+ const hubMethods = Array.isArray(tokenOrMethods) ? tokenOrMethods : (methods ?? []);
33
+ __classPrivateFieldGet(this, _ProcessingSignalRContext_instances, "m", _ProcessingSignalRContext_setupConnection).call(this, JUMOO_PROCESSING_HUB_PATH, hubMethods);
33
34
  }
34
35
  hostConnected() {
35
36
  super.hostConnected();
@@ -53,9 +54,11 @@ export class ProcessingSignalRContext extends UmbControllerBase {
53
54
  return __classPrivateFieldGet(this, _ProcessingSignalRContext_connection, "f")?.connectionId ?? null;
54
55
  }
55
56
  }
56
- _ProcessingSignalRContext_connection = new WeakMap(), _ProcessingSignalRContext_messages = new WeakMap(), _ProcessingSignalRContext_clientId = new WeakMap(), _ProcessingSignalRContext_connectionState = new WeakMap(), _ProcessingSignalRContext_instances = new WeakSet(), _ProcessingSignalRContext_setupConnection = function _ProcessingSignalRContext_setupConnection(url, token, methods) {
57
+ _ProcessingSignalRContext_connection = new WeakMap(), _ProcessingSignalRContext_messages = new WeakMap(), _ProcessingSignalRContext_clientId = new WeakMap(), _ProcessingSignalRContext_connectionState = new WeakMap(), _ProcessingSignalRContext_instances = new WeakSet(), _ProcessingSignalRContext_setupConnection = function _ProcessingSignalRContext_setupConnection(url, methods) {
58
+ // no accessTokenFactory: the backoffice auth cookie goes with the negotiate request and
59
+ // the websocket upgrade, which is how Umbraco's own server-event hub connects.
57
60
  __classPrivateFieldSet(this, _ProcessingSignalRContext_connection, new signalR.HubConnectionBuilder()
58
- .withUrl(url, { accessTokenFactory: () => token })
61
+ .withUrl(url)
59
62
  .withAutomaticReconnect([0, 2000, 10000, 30000])
60
63
  .configureLogging(signalR.LogLevel.None)
61
64
  .build(), "f");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jumoo/processing",
3
3
  "licence": "custom",
4
- "version": "18.3.0",
4
+ "version": "19.0.0-beta01",
5
5
  "type": "module",
6
6
  "description": "Jumoo processing library",
7
7
  "exports": {
@@ -40,7 +40,7 @@
40
40
  "@eslint/js": "^10.0.1",
41
41
  "@hey-api/openapi-ts": "^0.99.0",
42
42
  "@microsoft/signalr": "^10.0.0",
43
- "@umbraco-cms/backoffice": "18.0.1",
43
+ "@umbraco-cms/backoffice": "19.0.0-beta1",
44
44
  "chalk": "^5.6.2",
45
45
  "cross-env": "^10.1.0",
46
46
  "eslint": "^10.13.0",