@applicaster/zapp-react-native-utils 15.1.15-rc.0 → 15.1.16-rc.0

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.
@@ -56,7 +56,6 @@ export class ActionExecutor {
56
56
  context?: ActionExecutionContext
57
57
  ): Promise<ActionResult> => {
58
58
  for (const action of actionTypes) {
59
- // TODO: Handle action result error
60
59
  const result = await this.handleAction(action, context);
61
60
 
62
61
  if (result === ActionResult.Cancel) {
@@ -64,6 +63,16 @@ export class ActionExecutor {
64
63
 
65
64
  return result;
66
65
  }
66
+
67
+ if (result === ActionResult.Error) {
68
+ // Everything after a failed step was written for a state that never
69
+ // happened — the event was refused, the screen never opened — so
70
+ // running it is how a chain ends up telling the user their mail was
71
+ // sent when it was not.
72
+ log_error(`handleActions: action: ${action.type} failed`, { action });
73
+
74
+ return result;
75
+ }
67
76
  }
68
77
 
69
78
  return ActionResult.Success;
@@ -18,6 +18,7 @@ import { createLogger } from "../logger";
18
18
  import {
19
19
  appRestartAction,
20
20
  confirmDialogAction,
21
+ showAlertAction,
21
22
  createGoBackAction,
22
23
  createGoHomeAction,
23
24
  createNavigateToScreenAction,
@@ -109,6 +110,8 @@ const prepareDefaultActions = (actionExecutor) => {
109
110
  confirmDialogAction
110
111
  );
111
112
 
113
+ actionExecutor.registerAction(ACTION_TYPES.SHOW_ALERT, showAlertAction);
114
+
112
115
  actionExecutor.registerAction(
113
116
  ACTION_TYPES.SEND_CLOUD_EVENT,
114
117
  sendCloudEventAction
@@ -0,0 +1,71 @@
1
+ import { ActionExecutor, ActionResult } from "../ActionExecutor";
2
+
3
+ jest.mock("../../logger", () => ({
4
+ createLogger: () => ({
5
+ log_error: jest.fn(),
6
+ log_debug: jest.fn(),
7
+ log_info: jest.fn(),
8
+ }),
9
+ }));
10
+
11
+ describe("handleActions", () => {
12
+ const executor = () => {
13
+ const ran: string[] = [];
14
+ const actionExecutor = new ActionExecutor();
15
+
16
+ const register = (type: string, result?: ActionResult) =>
17
+ actionExecutor.registerAction(type, async () => {
18
+ ran.push(type);
19
+
20
+ return result as ActionResult;
21
+ });
22
+
23
+ register("ok", ActionResult.Success);
24
+ register("fails", ActionResult.Error);
25
+ register("cancels", ActionResult.Cancel);
26
+ register("recover", ActionResult.Success);
27
+ register("after", ActionResult.Success);
28
+
29
+ return { actionExecutor, ran };
30
+ };
31
+
32
+ // Everything after a failed step was written for a state that never
33
+ // happened: the event was refused, the screen never opened.
34
+ it("stops when an action fails", async () => {
35
+ const { actionExecutor, ran } = executor();
36
+
37
+ const result = await actionExecutor.handleActions([
38
+ { type: "fails" },
39
+ { type: "after" },
40
+ ]);
41
+
42
+ expect(ran).toEqual(["fails"]);
43
+ expect(result).toBe(ActionResult.Error);
44
+ });
45
+
46
+ it("still stops on a cancel", async () => {
47
+ const { actionExecutor, ran } = executor();
48
+
49
+ const result = await actionExecutor.handleActions([
50
+ { type: "cancels" },
51
+ { type: "after" },
52
+ ]);
53
+
54
+ expect(ran).toEqual(["cancels"]);
55
+ expect(result).toBe(ActionResult.Cancel);
56
+ });
57
+
58
+ // An action nobody registered never ran, which is a misconfigured feed
59
+ // rather than a step that failed. It is stepped over, as it always was.
60
+ it("steps over an action with no handler", async () => {
61
+ const { actionExecutor, ran } = executor();
62
+
63
+ const result = await actionExecutor.handleActions([
64
+ { type: "nobody-registered-this" },
65
+ { type: "after" },
66
+ ]);
67
+
68
+ expect(ran).toEqual(["after"]);
69
+ expect(result).toBe(ActionResult.Success);
70
+ });
71
+ });
@@ -77,6 +77,94 @@ describe("openBottomSheetAction inline items", () => {
77
77
  });
78
78
  });
79
79
 
80
+ it("opens an empty inline collection when dynamic collection options are present", async () => {
81
+ const result = await openBottomSheetAction({
82
+ type: "openBottomSheet",
83
+ options: {
84
+ header: { title: "Queue" },
85
+ content: {
86
+ items: [],
87
+ emptyMessage: "Queued items will be added here",
88
+ dynamicCollection: {
89
+ operations: ["remove"],
90
+ provider: "@{provider/queue}",
91
+ },
92
+ },
93
+ },
94
+ } as any);
95
+
96
+ expect(result).toBe(ActionResult.Success);
97
+
98
+ const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
99
+
100
+ expect(menu.content.items).toEqual([]);
101
+ expect(menu.content.emptyMessage).toBe("Queued items will be added here");
102
+
103
+ expect(menu.content.dynamicCollection).toMatchObject({
104
+ operations: ["remove"],
105
+ provider: "@{provider/queue}",
106
+ });
107
+ });
108
+
109
+ it("accepts empty inline items when dynamic_collection_options live on extensions", async () => {
110
+ const result = await openBottomSheetAction({
111
+ type: "openBottomSheet",
112
+ options: {
113
+ content: {
114
+ items: [],
115
+ extensions: {
116
+ dynamic_collection_options: {
117
+ operations: "remove,reorder",
118
+ provider: "@{provider/offline}",
119
+ },
120
+ },
121
+ },
122
+ },
123
+ } as any);
124
+
125
+ expect(result).toBe(ActionResult.Success);
126
+
127
+ const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
128
+
129
+ expect(menu.content.items).toEqual([]);
130
+
131
+ expect(menu.content.dynamicCollection).toMatchObject({
132
+ operations: ["remove", "reorder"],
133
+ provider: "@{provider/offline}",
134
+ });
135
+ });
136
+
137
+ it("returns an error for an empty items array with no source", async () => {
138
+ const result = await openBottomSheetAction({
139
+ type: "openBottomSheet",
140
+ options: {
141
+ content: { items: [] },
142
+ },
143
+ } as any);
144
+
145
+ expect(result).toBe(ActionResult.Error);
146
+ expect(modalOrchestrator.open).not.toHaveBeenCalled();
147
+ });
148
+
149
+ it("forwards content.emptyMessage onto the menu", async () => {
150
+ const result = await openBottomSheetAction({
151
+ type: "openBottomSheet",
152
+ options: {
153
+ header: { title: "Queue" },
154
+ content: {
155
+ items: [{ id: "t1", title: "Track 1" }],
156
+ emptyMessage: "Queued items will be added here",
157
+ },
158
+ },
159
+ } as any);
160
+
161
+ expect(result).toBe(ActionResult.Success);
162
+
163
+ const [menu] = (modalOrchestrator.open as jest.Mock).mock.calls[0];
164
+
165
+ expect(menu.content.emptyMessage).toBe("Queued items will be added here");
166
+ });
167
+
80
168
  it("pushes a new sheet by default even when another sheet is already open", async () => {
81
169
  (modalOrchestrator as any).stackSize = 1;
82
170
 
@@ -0,0 +1,49 @@
1
+ import { showAlertAction } from "../showAlert";
2
+ import { ActionResult } from "../../ActionExecutor";
3
+ import { showAlertDialog } from "../../../alertUtils";
4
+
5
+ jest.mock("../../../alertUtils", () => ({
6
+ showAlertDialog: jest.fn(),
7
+ }));
8
+
9
+ const dialog = showAlertDialog as jest.Mock;
10
+
11
+ const dismiss = () => dialog.mock.calls[0][0].completion();
12
+
13
+ describe("showAlert", () => {
14
+ beforeEach(() => dialog.mockReset());
15
+
16
+ it("shows the notice and resolves once it is dismissed", async () => {
17
+ const result = showAlertAction({
18
+ type: "showAlert",
19
+ options: { title: "Locked", message: "Ask the account owner." },
20
+ });
21
+
22
+ expect(dialog).toHaveBeenCalledWith(
23
+ expect.objectContaining({
24
+ title: "Locked",
25
+ message: "Ask the account owner.",
26
+ okButtonText: "OK",
27
+ })
28
+ );
29
+
30
+ dismiss();
31
+
32
+ // Dismissing a notice is not a cancellation: the chain carries on.
33
+ await expect(result).resolves.toBe(ActionResult.Success);
34
+ });
35
+
36
+ it("takes the dismiss label from the action", async () => {
37
+ const result = showAlertAction({
38
+ type: "showAlert",
39
+ options: { title: "Locked", okButtonText: "Got it" },
40
+ });
41
+
42
+ expect(dialog).toHaveBeenCalledWith(
43
+ expect.objectContaining({ okButtonText: "Got it" })
44
+ );
45
+
46
+ dismiss();
47
+ await result;
48
+ });
49
+ });
@@ -15,6 +15,8 @@ export { appRestartAction } from "./appRestart";
15
15
 
16
16
  export { confirmDialogAction } from "./confirmDialog";
17
17
 
18
+ export { showAlertAction } from "./showAlert";
19
+
18
20
  export { sendCloudEventAction } from "./sendCloudEvent";
19
21
 
20
22
  export { sessionStorageToggleFlagAction } from "./sessionStorageToggleFlag";
@@ -227,7 +227,18 @@ export async function openBottomSheetAction(
227
227
 
228
228
  let dynamicCollection: DynamicCollectionOptions | undefined;
229
229
 
230
- if (Array.isArray(inlineItems) && inlineItems.length > 0) {
230
+ const rawDynamicCollection =
231
+ content?.dynamicCollection ??
232
+ content?.extensions?.dynamic_collection_options;
233
+
234
+ // An empty items array is a valid starting state for a provider-backed or
235
+ // offline collection. A sheet with neither items nor a source still errors.
236
+ const hasDynamicCollection = Boolean(rawDynamicCollection);
237
+
238
+ if (
239
+ Array.isArray(inlineItems) &&
240
+ (inlineItems.length > 0 || hasDynamicCollection)
241
+ ) {
231
242
  log_info("openBottomSheet: using inline items from action options", {
232
243
  count: inlineItems.length,
233
244
  });
@@ -239,10 +250,7 @@ export async function openBottomSheetAction(
239
250
  entryToMenuItem(entry, behavior?.currentSelection ?? [])
240
251
  );
241
252
 
242
- dynamicCollection = parseDynamicCollectionOptions(
243
- content?.dynamicCollection ??
244
- content?.extensions?.dynamic_collection_options
245
- );
253
+ dynamicCollection = parseDynamicCollectionOptions(rawDynamicCollection);
246
254
  } else if (itemsUrl) {
247
255
  log_info(`openBottomSheet: loading items from source: ${itemsUrl}`);
248
256
 
@@ -280,6 +288,7 @@ export async function openBottomSheetAction(
280
288
  dynamicCollection,
281
289
  themePluginId,
282
290
  styleOverrides: styleOverrides ?? content?.styleOverrides,
291
+ emptyMessage: content?.emptyMessage,
283
292
  context,
284
293
  },
285
294
  };
@@ -0,0 +1,51 @@
1
+ /// <reference types="@applicaster/applicaster-types" />
2
+ import { showAlertDialog } from "../../alertUtils";
3
+ import { ActionResult } from "../ActionExecutor";
4
+ import { ActionHandler } from "../types";
5
+ import { createLogger } from "../../logger";
6
+
7
+ const { log_info } = createLogger({
8
+ subsystem: "ActionExecutorContext",
9
+ category: "General",
10
+ });
11
+
12
+ /**
13
+ * Options for the showAlert action.
14
+ */
15
+ export interface ShowAlertOptions {
16
+ /** Dialog title (required) */
17
+ title: string;
18
+
19
+ /** Dialog message */
20
+ message?: string;
21
+
22
+ /** Dismiss button text. Defaults to "OK" */
23
+ okButtonText?: string;
24
+ }
25
+
26
+ /**
27
+ * Tells the user something and waits for them to acknowledge it.
28
+ *
29
+ * One button, because there is nothing to decide. Use `confirmDialog` when the
30
+ * user is choosing between going on and backing out; a `confirmDialog` without
31
+ * a cancel label still renders two buttons, the second one blank.
32
+ *
33
+ * Always resolves to Success: dismissing a notice is not a cancellation, so the
34
+ * rest of the chain runs.
35
+ */
36
+ export const showAlertAction: ActionHandler<ShowAlertOptions> = async (
37
+ action
38
+ ): Promise<ActionResult> => {
39
+ log_info("handleAction: showAlert event");
40
+
41
+ const options = action.options as ShowAlertOptions;
42
+
43
+ return new Promise<ActionResult>((resolve) => {
44
+ showAlertDialog({
45
+ title: options?.title,
46
+ message: options?.message,
47
+ okButtonText: options?.okButtonText || "OK",
48
+ completion: () => resolve(ActionResult.Success),
49
+ });
50
+ });
51
+ };
@@ -20,6 +20,7 @@ export const ACTION_TYPES = {
20
20
  SWITCH_LAYOUT: "switchLayout",
21
21
  APP_RESTART: "appRestart",
22
22
  CONFIRM_DIALOG: "confirmDialog",
23
+ SHOW_ALERT: "showAlert",
23
24
  SCREEN_SET_VARIABLE: "screenSetVariable",
24
25
  SCREEN_TOGGLE_FLAG: "screenToggleFlag",
25
26
  } as const;
@@ -0,0 +1,67 @@
1
+ import { getUpcomingQueue } from "../getUpcomingQueue";
2
+
3
+ describe("getUpcomingQueue", () => {
4
+ it("returns null when plugin is not provided or has no module", async () => {
5
+ expect(await getUpcomingQueue(undefined)).toBeNull();
6
+ expect(await getUpcomingQueue({} as any)).toBeNull();
7
+ });
8
+
9
+ it("returns null when QueueManager is not present on module", async () => {
10
+ const plugin = {
11
+ identifier: "queue-action",
12
+ module: {},
13
+ } as any;
14
+
15
+ expect(await getUpcomingQueue(plugin)).toBeNull();
16
+ });
17
+
18
+ it("calls setupQuickBrickQueue when QueueManager is not initialized", async () => {
19
+ const setupQuickBrickQueue = jest.fn();
20
+ const mockUpcoming = [{ id: "item_1" }];
21
+
22
+ const QueueManager = {
23
+ isInitialized: jest
24
+ .fn()
25
+ .mockReturnValueOnce(false)
26
+ .mockReturnValueOnce(true),
27
+ instance: {
28
+ getUpcomingQueue: jest.fn().mockResolvedValue(mockUpcoming),
29
+ },
30
+ };
31
+
32
+ const plugin = {
33
+ identifier: "queue-action",
34
+ module: {
35
+ QueueManager,
36
+ setupQuickBrickQueue,
37
+ },
38
+ } as any;
39
+
40
+ const result = await getUpcomingQueue(plugin);
41
+
42
+ expect(setupQuickBrickQueue).toHaveBeenCalled();
43
+ expect(result).toEqual(mockUpcoming);
44
+ });
45
+
46
+ it("returns upcoming items when QueueManager is already initialized", async () => {
47
+ const mockUpcoming = [{ id: "item_1" }, { id: "item_2" }];
48
+
49
+ const QueueManager = {
50
+ isInitialized: jest.fn().mockReturnValue(true),
51
+ instance: {
52
+ getUpcomingQueue: jest.fn().mockResolvedValue(mockUpcoming),
53
+ },
54
+ };
55
+
56
+ const plugin = {
57
+ identifier: "queue-action",
58
+ module: {
59
+ QueueManager,
60
+ },
61
+ } as any;
62
+
63
+ const result = await getUpcomingQueue(plugin);
64
+
65
+ expect(result).toEqual(mockUpcoming);
66
+ });
67
+ });
@@ -1,9 +1,22 @@
1
1
  export const getUpcomingQueue = async (
2
2
  plugin?: QuickBrickPlugin | ZappPlugin
3
3
  ): Promise<unknown[] | null> => {
4
- const QueueManager = (plugin as any)?.module?.QueueManager;
4
+ const module = (plugin as any)?.module;
5
+ const QueueManager = module?.QueueManager;
6
+ const setupQuickBrickQueue = module?.setupQuickBrickQueue;
5
7
 
6
- if (!QueueManager?.instance) {
8
+ if (!QueueManager) {
9
+ return null;
10
+ }
11
+
12
+ if (
13
+ !QueueManager.isInitialized?.() &&
14
+ typeof setupQuickBrickQueue === "function"
15
+ ) {
16
+ setupQuickBrickQueue();
17
+ }
18
+
19
+ if (!QueueManager.isInitialized?.()) {
7
20
  return null;
8
21
  }
9
22
 
@@ -205,56 +205,64 @@ describe("parseModalBlocksConfiguration", () => {
205
205
  );
206
206
  });
207
207
 
208
- it("should fall back to flavor1 reorder/remove assets", () => {
208
+ it("should parse section_header title and action_buttons independently of header", () => {
209
209
  const result = parseModalBlocksConfiguration(
210
210
  {
211
- "playlist_experience_assets_flavor1.remove_from_playlist_asset":
212
- "https://placehold.co/24x24/00FF00/000000/png?text=RM",
213
- "playlist_experience_assets_flavor1.reorder_handle_asset":
214
- "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
211
+ "section_header.title.font_color": "#ABCDEF",
212
+ "section_header.title.font_size": 13,
213
+ "section_header.title.ios.font_family": "SFProText-Bold",
214
+ "section_header.action_buttons.default.background_color":
215
+ "rgba(62, 62, 62, 1)",
216
+ "section_header.action_buttons.focus.background_color":
217
+ "rgba(78, 78, 78, 1)",
218
+ "section_header.action_buttons.default.font_color": "#FFFFFF",
219
+ "section_header.action_buttons.font_size": 14,
220
+ "section_header.action_buttons.border_radius": 16,
221
+ // Theme modal header — must not leak into sectionHeader
222
+ queue_action_style_header_title_default_fontColor: "#111111",
223
+ queue_action_style_header_title_default_fontSize: 22,
215
224
  },
216
- "playlist-experience"
225
+ "queue_action"
217
226
  );
218
227
 
219
- expect(result.item.actionButtons.default).toEqual(
228
+ expect(result.sectionHeader.title.default).toEqual(
220
229
  expect.objectContaining({
221
- removeAsset: "https://placehold.co/24x24/00FF00/000000/png?text=RM",
222
- dragAsset: "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
230
+ fontColor: "#ABCDEF",
231
+ fontSize: 13,
232
+ fontFamily: "SFProText-Bold",
223
233
  })
224
234
  );
225
- });
226
235
 
227
- it("should fall back to flavor2 reorder/remove assets", () => {
228
- const result = parseModalBlocksConfiguration(
229
- {
230
- "playlist_experience_assets_flavor2.remove_from_playlist_asset":
231
- "https://placehold.co/24x24/00FF00/000000/png?text=RM",
232
- "playlist_experience_assets_flavor2.reorder_handle_asset":
233
- "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
234
- },
235
- "playlist-experience"
236
+ expect(result.sectionHeader.actionButtons.default).toEqual(
237
+ expect.objectContaining({
238
+ backgroundColor: "rgba(62, 62, 62, 1)",
239
+ fontColor: "#FFFFFF",
240
+ fontSize: 14,
241
+ borderRadius: 16,
242
+ })
236
243
  );
237
244
 
238
- expect(result.item.actionButtons.default).toEqual(
245
+ expect(result.sectionHeader.actionButtons.focused).toEqual(
239
246
  expect.objectContaining({
240
- removeAsset: "https://placehold.co/24x24/00FF00/000000/png?text=RM",
241
- dragAsset: "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
247
+ backgroundColor: "rgba(78, 78, 78, 1)",
248
+ })
249
+ );
250
+
251
+ expect(result.header.title.default).toEqual(
252
+ expect.objectContaining({
253
+ fontColor: "#111111",
254
+ fontSize: 22,
242
255
  })
243
256
  );
244
257
  });
245
258
 
246
- it("should ignore playlist-only asset keys for other theme plugins", () => {
259
+ it("should leave sectionHeader empty when keys are absent", () => {
247
260
  const result = parseModalBlocksConfiguration(
248
- {
249
- "button.create_new_playlist_asset":
250
- "https://placehold.co/24x24/FF00FF/FFFFFF/png?text=CN",
251
- "playlist_experience_assets_flavor1.reorder_handle_asset":
252
- "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
253
- },
254
- "queue-action"
261
+ { "item.text_label1.font_color": "#FFFFFF" },
262
+ "playlist-experience"
255
263
  );
256
264
 
257
- expect(result.button.leadingIcon.default?.asset).toBeUndefined();
258
- expect(result.item.actionButtons.default?.dragAsset).toBeUndefined();
265
+ expect(result.sectionHeader.title).toEqual({ default: {} });
266
+ expect(result.sectionHeader.actionButtons).toEqual({ default: {} });
259
267
  });
260
268
  });
@@ -7,6 +7,14 @@ export type ModalBlocksConfig = {
7
7
  closeButton: Record<string, any>;
8
8
  actionButtons: Record<string, any>;
9
9
  };
10
+ /**
11
+ * Queue Section Header (BB Variant 3) — shared by Next in Queue / Next Up.
12
+ * Separate from Theme modal `header` so top-title styles do not collide.
13
+ */
14
+ sectionHeader: {
15
+ title: Record<string, any>;
16
+ actionButtons: Record<string, any>;
17
+ };
10
18
  action: {
11
19
  background: Record<string, any>;
12
20
  title: Record<string, any>;
@@ -63,6 +71,10 @@ export const parseModalBlocksConfiguration = (
63
71
  closeButton: {},
64
72
  actionButtons: {},
65
73
  },
74
+ sectionHeader: {
75
+ title: {},
76
+ actionButtons: {},
77
+ },
66
78
  action: {
67
79
  background: {},
68
80
  title: {},
@@ -148,6 +160,8 @@ export const parseModalBlocksConfiguration = (
148
160
  "action_buttons",
149
161
  ]);
150
162
 
163
+ parseBlock("section_header", ["title", "action_buttons"]);
164
+
151
165
  parseBlock("action", [
152
166
  "background",
153
167
  "title",
@@ -44,6 +44,7 @@ function menuToModalArgs(
44
44
  title: menu.header?.title ?? menu.content.title,
45
45
  summary: menu.header?.subtitle,
46
46
  styleOverrides: menu.content.styleOverrides,
47
+ emptyMessage: menu.content.emptyMessage,
47
48
  onPress: onItemPress,
48
49
  viewModel,
49
50
  },
@@ -44,4 +44,20 @@ describe("ModalOrchestrator phase 3", () => {
44
44
  expect(props.title).toBe("Sleep Timer");
45
45
  expect(props.summary).toBe("remaining");
46
46
  });
47
+
48
+ it("forwards the queue empty message onto the sheet props", () => {
49
+ modalOrchestrator.open({
50
+ header: { title: "Queue" },
51
+ content: {
52
+ title: "Queue",
53
+ items: [],
54
+ emptyMessage: "Queued items will be added here",
55
+ },
56
+ });
57
+
58
+ const props = (modalStore.getState().modalState.screen as any)
59
+ ?.modalBottomSheetContentProps;
60
+
61
+ expect(props.emptyMessage).toBe("Queued items will be added here");
62
+ });
47
63
  });
@@ -118,6 +118,8 @@ export interface Content {
118
118
  dynamicCollection?: DynamicCollectionOptions;
119
119
  themePluginId?: string;
120
120
  styleOverrides?: Record<string, any>;
121
+ /** Shown under Next Up when the queue sheet has no upcoming items. */
122
+ emptyMessage?: string;
121
123
  context?: Record<string, any>;
122
124
  }
123
125
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "15.1.15-rc.0",
3
+ "version": "15.1.16-rc.0",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "homepage": "https://github.com/applicaster/quickbrick#readme",
29
29
  "dependencies": {
30
- "@applicaster/applicaster-types": "15.1.15-rc.0",
30
+ "@applicaster/applicaster-types": "15.1.16-rc.0",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",