@statezero/core 0.2.22 → 0.2.25

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 (191) hide show
  1. package/dist/actions/backend1/django_app/calculate-hash.d.ts +57 -0
  2. package/dist/actions/backend1/django_app/calculate-hash.js +80 -0
  3. package/dist/actions/backend1/django_app/calculate-hash.schema.json +148 -0
  4. package/dist/actions/backend1/django_app/get-current-username.d.ts +29 -0
  5. package/dist/actions/backend1/django_app/get-current-username.js +65 -0
  6. package/dist/actions/backend1/django_app/get-current-username.schema.json +47 -0
  7. package/dist/actions/backend1/django_app/get-server-status.d.ts +38 -0
  8. package/dist/actions/backend1/django_app/get-server-status.js +68 -0
  9. package/dist/actions/backend1/django_app/get-server-status.schema.json +93 -0
  10. package/dist/actions/backend1/django_app/get-user-info.d.ts +44 -0
  11. package/dist/actions/backend1/django_app/get-user-info.js +70 -0
  12. package/dist/actions/backend1/django_app/get-user-info.schema.json +127 -0
  13. package/dist/actions/backend1/django_app/index.d.ts +1 -0
  14. package/dist/actions/backend1/django_app/index.js +6 -0
  15. package/dist/actions/backend1/django_app/process-data.d.ts +51 -0
  16. package/dist/actions/backend1/django_app/process-data.js +78 -0
  17. package/dist/actions/backend1/django_app/process-data.schema.json +117 -0
  18. package/dist/actions/backend1/django_app/send-notification.d.ts +55 -0
  19. package/dist/actions/backend1/django_app/send-notification.js +81 -0
  20. package/dist/actions/backend1/django_app/send-notification.schema.json +175 -0
  21. package/dist/actions/backend1/index.d.ts +1 -0
  22. package/dist/actions/backend1/index.js +1 -0
  23. package/dist/actions/default/django_app/calculate-hash.d.ts +57 -0
  24. package/dist/actions/default/django_app/calculate-hash.js +80 -0
  25. package/dist/actions/default/django_app/calculate-hash.schema.json +148 -0
  26. package/dist/actions/default/django_app/get-current-username.d.ts +29 -0
  27. package/dist/actions/default/django_app/get-current-username.js +65 -0
  28. package/dist/actions/default/django_app/get-current-username.schema.json +47 -0
  29. package/dist/actions/default/django_app/get-server-status.d.ts +38 -0
  30. package/dist/actions/default/django_app/get-server-status.js +68 -0
  31. package/dist/actions/default/django_app/get-server-status.schema.json +93 -0
  32. package/dist/actions/default/django_app/get-user-info.d.ts +44 -0
  33. package/dist/actions/default/django_app/get-user-info.js +70 -0
  34. package/dist/actions/default/django_app/get-user-info.schema.json +127 -0
  35. package/dist/actions/default/django_app/index.d.ts +1 -0
  36. package/dist/actions/default/django_app/index.js +6 -0
  37. package/dist/actions/default/django_app/process-data.d.ts +51 -0
  38. package/dist/actions/default/django_app/process-data.js +78 -0
  39. package/dist/actions/default/django_app/process-data.schema.json +117 -0
  40. package/dist/actions/default/django_app/send-notification.d.ts +55 -0
  41. package/dist/actions/default/django_app/send-notification.js +81 -0
  42. package/dist/actions/default/django_app/send-notification.schema.json +175 -0
  43. package/dist/actions/default/index.d.ts +1 -0
  44. package/dist/actions/default/index.js +1 -0
  45. package/dist/actions/index.d.ts +1 -0
  46. package/dist/actions/index.js +5 -0
  47. package/dist/filtering/localFiltering.d.ts +1 -8
  48. package/dist/filtering/localFiltering.js +123 -47
  49. package/dist/flavours/django/dates.js +4 -1
  50. package/dist/flavours/django/serializers.js +8 -4
  51. package/dist/models/backend1/django_app/comprehensivemodel.d.ts +894 -0
  52. package/dist/models/backend1/django_app/comprehensivemodel.js +71 -0
  53. package/dist/models/backend1/django_app/comprehensivemodel.schema.json +870 -0
  54. package/dist/models/backend1/django_app/custompkmodel.d.ts +92 -0
  55. package/dist/models/backend1/django_app/custompkmodel.js +69 -0
  56. package/dist/models/backend1/django_app/custompkmodel.schema.json +71 -0
  57. package/dist/models/backend1/django_app/dailyrate.d.ts +230 -0
  58. package/dist/models/backend1/django_app/dailyrate.js +71 -0
  59. package/dist/models/backend1/django_app/dailyrate.schema.json +212 -0
  60. package/dist/models/backend1/django_app/deepmodellevel1.d.ts +140 -0
  61. package/dist/models/backend1/django_app/deepmodellevel1.js +72 -0
  62. package/dist/models/backend1/django_app/deepmodellevel1.schema.json +114 -0
  63. package/dist/models/backend1/django_app/deepmodellevel2.d.ts +118 -0
  64. package/dist/models/backend1/django_app/deepmodellevel2.js +71 -0
  65. package/dist/models/backend1/django_app/deepmodellevel2.schema.json +92 -0
  66. package/dist/models/backend1/django_app/deepmodellevel3.d.ts +92 -0
  67. package/dist/models/backend1/django_app/deepmodellevel3.js +69 -0
  68. package/dist/models/backend1/django_app/deepmodellevel3.schema.json +69 -0
  69. package/dist/models/backend1/django_app/dummymodel.d.ts +134 -0
  70. package/dist/models/backend1/django_app/dummymodel.js +71 -0
  71. package/dist/models/backend1/django_app/dummymodel.schema.json +109 -0
  72. package/dist/models/backend1/django_app/dummyrelatedmodel.d.ts +92 -0
  73. package/dist/models/backend1/django_app/dummyrelatedmodel.js +69 -0
  74. package/dist/models/backend1/django_app/dummyrelatedmodel.schema.json +69 -0
  75. package/dist/models/backend1/django_app/filetest.d.ts +140 -0
  76. package/dist/models/backend1/django_app/filetest.js +69 -0
  77. package/dist/models/backend1/django_app/filetest.schema.json +111 -0
  78. package/dist/models/backend1/django_app/index.d.ts +1 -0
  79. package/dist/models/backend1/django_app/index.js +21 -0
  80. package/dist/models/backend1/django_app/m2mdepthtestlevel1.d.ts +118 -0
  81. package/dist/models/backend1/django_app/m2mdepthtestlevel1.js +71 -0
  82. package/dist/models/backend1/django_app/m2mdepthtestlevel1.schema.json +94 -0
  83. package/dist/models/backend1/django_app/m2mdepthtestlevel2.d.ts +118 -0
  84. package/dist/models/backend1/django_app/m2mdepthtestlevel2.js +71 -0
  85. package/dist/models/backend1/django_app/m2mdepthtestlevel2.schema.json +94 -0
  86. package/dist/models/backend1/django_app/m2mdepthtestlevel3.d.ts +134 -0
  87. package/dist/models/backend1/django_app/m2mdepthtestlevel3.js +71 -0
  88. package/dist/models/backend1/django_app/m2mdepthtestlevel3.schema.json +112 -0
  89. package/dist/models/backend1/django_app/modelwithcustompkrelation.d.ts +118 -0
  90. package/dist/models/backend1/django_app/modelwithcustompkrelation.js +71 -0
  91. package/dist/models/backend1/django_app/modelwithcustompkrelation.schema.json +93 -0
  92. package/dist/models/backend1/django_app/modelwithrestrictedfields.d.ts +134 -0
  93. package/dist/models/backend1/django_app/modelwithrestrictedfields.js +71 -0
  94. package/dist/models/backend1/django_app/modelwithrestrictedfields.schema.json +111 -0
  95. package/dist/models/backend1/django_app/namefiltercustompkmodel.d.ts +92 -0
  96. package/dist/models/backend1/django_app/namefiltercustompkmodel.js +69 -0
  97. package/dist/models/backend1/django_app/namefiltercustompkmodel.schema.json +71 -0
  98. package/dist/models/backend1/django_app/order.d.ts +220 -0
  99. package/dist/models/backend1/django_app/order.js +71 -0
  100. package/dist/models/backend1/django_app/order.schema.json +203 -0
  101. package/dist/models/backend1/django_app/orderitem.d.ts +172 -0
  102. package/dist/models/backend1/django_app/orderitem.js +72 -0
  103. package/dist/models/backend1/django_app/orderitem.schema.json +149 -0
  104. package/dist/models/backend1/django_app/product.d.ts +254 -0
  105. package/dist/models/backend1/django_app/product.js +71 -0
  106. package/dist/models/backend1/django_app/product.schema.json +277 -0
  107. package/dist/models/backend1/django_app/productcategory.d.ts +92 -0
  108. package/dist/models/backend1/django_app/productcategory.js +69 -0
  109. package/dist/models/backend1/django_app/productcategory.schema.json +70 -0
  110. package/dist/models/backend1/django_app/rateplan.d.ts +92 -0
  111. package/dist/models/backend1/django_app/rateplan.js +69 -0
  112. package/dist/models/backend1/django_app/rateplan.schema.json +70 -0
  113. package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
  114. package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.js +69 -0
  115. package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
  116. package/dist/models/backend1/fileobject.d.ts +4 -0
  117. package/dist/models/backend1/fileobject.js +9 -0
  118. package/dist/models/backend1/index.d.ts +2 -0
  119. package/dist/models/backend1/index.js +2 -0
  120. package/dist/models/default/django_app/comprehensivemodel.d.ts +894 -0
  121. package/dist/models/default/django_app/comprehensivemodel.js +71 -0
  122. package/dist/models/default/django_app/comprehensivemodel.schema.json +870 -0
  123. package/dist/models/default/django_app/custompkmodel.d.ts +92 -0
  124. package/dist/models/default/django_app/custompkmodel.js +69 -0
  125. package/dist/models/default/django_app/custompkmodel.schema.json +71 -0
  126. package/dist/models/default/django_app/dailyrate.d.ts +230 -0
  127. package/dist/models/default/django_app/dailyrate.js +71 -0
  128. package/dist/models/default/django_app/dailyrate.schema.json +212 -0
  129. package/dist/models/default/django_app/deepmodellevel1.d.ts +128 -0
  130. package/dist/models/default/django_app/deepmodellevel1.js +72 -0
  131. package/dist/models/default/django_app/deepmodellevel1.schema.json +102 -0
  132. package/dist/models/default/django_app/deepmodellevel2.d.ts +106 -0
  133. package/dist/models/default/django_app/deepmodellevel2.js +71 -0
  134. package/dist/models/default/django_app/deepmodellevel2.schema.json +80 -0
  135. package/dist/models/default/django_app/deepmodellevel3.d.ts +80 -0
  136. package/dist/models/default/django_app/deepmodellevel3.js +69 -0
  137. package/dist/models/default/django_app/deepmodellevel3.schema.json +57 -0
  138. package/dist/models/default/django_app/dummymodel.d.ts +122 -0
  139. package/dist/models/default/django_app/dummymodel.js +71 -0
  140. package/dist/models/default/django_app/dummymodel.schema.json +97 -0
  141. package/dist/models/default/django_app/dummyrelatedmodel.d.ts +80 -0
  142. package/dist/models/default/django_app/dummyrelatedmodel.js +69 -0
  143. package/dist/models/default/django_app/dummyrelatedmodel.schema.json +57 -0
  144. package/dist/models/default/django_app/filetest.d.ts +128 -0
  145. package/dist/models/default/django_app/filetest.js +69 -0
  146. package/dist/models/default/django_app/filetest.schema.json +99 -0
  147. package/dist/models/default/django_app/index.d.ts +1 -0
  148. package/dist/models/default/django_app/index.js +21 -0
  149. package/dist/models/default/django_app/m2mdepthtestlevel1.d.ts +118 -0
  150. package/dist/models/default/django_app/m2mdepthtestlevel1.js +71 -0
  151. package/dist/models/default/django_app/m2mdepthtestlevel1.schema.json +94 -0
  152. package/dist/models/default/django_app/m2mdepthtestlevel2.d.ts +118 -0
  153. package/dist/models/default/django_app/m2mdepthtestlevel2.js +71 -0
  154. package/dist/models/default/django_app/m2mdepthtestlevel2.schema.json +94 -0
  155. package/dist/models/default/django_app/m2mdepthtestlevel3.d.ts +134 -0
  156. package/dist/models/default/django_app/m2mdepthtestlevel3.js +71 -0
  157. package/dist/models/default/django_app/m2mdepthtestlevel3.schema.json +112 -0
  158. package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +118 -0
  159. package/dist/models/default/django_app/modelwithcustompkrelation.js +71 -0
  160. package/dist/models/default/django_app/modelwithcustompkrelation.schema.json +93 -0
  161. package/dist/models/default/django_app/modelwithrestrictedfields.d.ts +134 -0
  162. package/dist/models/default/django_app/modelwithrestrictedfields.js +71 -0
  163. package/dist/models/default/django_app/modelwithrestrictedfields.schema.json +111 -0
  164. package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +92 -0
  165. package/dist/models/default/django_app/namefiltercustompkmodel.js +69 -0
  166. package/dist/models/default/django_app/namefiltercustompkmodel.schema.json +71 -0
  167. package/dist/models/default/django_app/order.d.ts +220 -0
  168. package/dist/models/default/django_app/order.js +71 -0
  169. package/dist/models/default/django_app/order.schema.json +203 -0
  170. package/dist/models/default/django_app/orderitem.d.ts +172 -0
  171. package/dist/models/default/django_app/orderitem.js +72 -0
  172. package/dist/models/default/django_app/orderitem.schema.json +149 -0
  173. package/dist/models/default/django_app/product.d.ts +254 -0
  174. package/dist/models/default/django_app/product.js +71 -0
  175. package/dist/models/default/django_app/product.schema.json +277 -0
  176. package/dist/models/default/django_app/productcategory.d.ts +92 -0
  177. package/dist/models/default/django_app/productcategory.js +69 -0
  178. package/dist/models/default/django_app/productcategory.schema.json +70 -0
  179. package/dist/models/default/django_app/rateplan.d.ts +92 -0
  180. package/dist/models/default/django_app/rateplan.js +69 -0
  181. package/dist/models/default/django_app/rateplan.schema.json +70 -0
  182. package/dist/models/default/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
  183. package/dist/models/default/django_app/restrictedfieldrelatedmodel.js +69 -0
  184. package/dist/models/default/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
  185. package/dist/models/default/fileobject.d.ts +4 -0
  186. package/dist/models/default/fileobject.js +9 -0
  187. package/dist/models/default/index.d.ts +2 -0
  188. package/dist/models/default/index.js +2 -0
  189. package/dist/models/index.d.ts +1 -0
  190. package/dist/models/index.js +5 -0
  191. package/package.json +1 -1
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Send notifications to multiple recipients
3
+ *
4
+ * @param string message - Notification message to send
5
+ * @param any[] recipients - List of email addresses to notify
6
+ * @param 'low' | 'high' priority - Notification priority level
7
+ * @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
8
+ * @returns {Promise<Object>} A promise that resolves with the action's result.
9
+ */
10
+ export function sendNotification(message: any, recipients: any, priority?: string, axiosOverrides?: Object): Promise<Object>;
11
+ export namespace sendNotification {
12
+ let actionName: string;
13
+ let title: string;
14
+ let app: string;
15
+ let permissions: string[];
16
+ let configKey: string;
17
+ }
18
+ /**
19
+ * Zod schema for the input of sendNotification.
20
+ * NOTE: This is an object schema for validating the data payload.
21
+ */
22
+ export const sendNotificationInputSchema: z.ZodObject<{
23
+ message: z.ZodString;
24
+ recipients: z.ZodArray<z.ZodAny, "many">;
25
+ priority: z.ZodDefault<z.ZodOptional<z.ZodEnum<["low", "high"]>>>;
26
+ }, "strip", z.ZodTypeAny, {
27
+ message: string;
28
+ priority: "low" | "high";
29
+ recipients: any[];
30
+ }, {
31
+ message: string;
32
+ recipients: any[];
33
+ priority?: "low" | "high" | undefined;
34
+ }>;
35
+ /**
36
+ * Zod schema for the response of sendNotification.
37
+ */
38
+ export const sendNotificationResponseSchema: z.ZodObject<{
39
+ success: z.ZodBoolean;
40
+ message_id: z.ZodString;
41
+ sent_to: z.ZodNumber;
42
+ queued_at: z.ZodString;
43
+ }, "strip", z.ZodTypeAny, {
44
+ success: boolean;
45
+ message_id: string;
46
+ sent_to: number;
47
+ queued_at: string;
48
+ }, {
49
+ success: boolean;
50
+ message_id: string;
51
+ sent_to: number;
52
+ queued_at: string;
53
+ }>;
54
+ export default sendNotification;
55
+ import { z } from 'zod';
@@ -0,0 +1,81 @@
1
+ /**
2
+ * This file was auto-generated. Do not make direct changes to the file.
3
+ * Action: Send Notification
4
+ * App: django_app
5
+ */
6
+ import axios from 'axios';
7
+ import { z } from 'zod';
8
+ import { configInstance, parseStateZeroError, serializeActionPayload } from '../../../../src';
9
+ import actionSchema from './send-notification.schema.json' assert { type: 'json' };
10
+ /**
11
+ * Zod schema for the input of sendNotification.
12
+ * NOTE: This is an object schema for validating the data payload.
13
+ */
14
+ export const sendNotificationInputSchema = z.object({ message: z.string().max(500),
15
+ recipients: z.array(z.any()),
16
+ priority: z.enum(["low", "high"]).optional().default("low") });
17
+ /**
18
+ * Zod schema for the response of sendNotification.
19
+ */
20
+ export const sendNotificationResponseSchema = z.object({ success: z.boolean(),
21
+ message_id: z.string(),
22
+ sent_to: z.number().int(),
23
+ queued_at: z.string().datetime({ message: "Invalid ISO 8601 datetime string" }) });
24
+ /**
25
+ * Send notifications to multiple recipients
26
+ *
27
+ * @param string message - Notification message to send
28
+ * @param any[] recipients - List of email addresses to notify
29
+ * @param 'low' | 'high' priority - Notification priority level
30
+ * @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
31
+ * @returns {Promise<Object>} A promise that resolves with the action's result.
32
+ */
33
+ export async function sendNotification(message, recipients, priority = "low", axiosOverrides = {}) {
34
+ // Construct the data payload from the function arguments
35
+ const rawPayload = {
36
+ message,
37
+ recipients,
38
+ priority
39
+ };
40
+ // Serialize payload - handles model instances (extracts PK), files, dates, etc.
41
+ const payload = serializeActionPayload(rawPayload, actionSchema.input_properties);
42
+ const config = configInstance.getConfig();
43
+ const backend = config.backendConfigs['default'];
44
+ if (!backend) {
45
+ throw new Error(`No backend configuration found for key: default`);
46
+ }
47
+ const baseUrl = backend.API_URL.replace(/\/+$/, '');
48
+ const actionUrl = `${baseUrl}/actions/send_notification/`;
49
+ const headers = backend.getAuthHeaders ? backend.getAuthHeaders() : {};
50
+ try {
51
+ const response = await axios.post(actionUrl, payload, {
52
+ headers: { 'Content-Type': 'application/json', ...headers },
53
+ ...axiosOverrides,
54
+ });
55
+ return sendNotificationResponseSchema.parse(response.data);
56
+ }
57
+ catch (error) {
58
+ if (error instanceof z.ZodError) {
59
+ throw new Error(`Send Notification failed: Invalid response from server. Details: ${error.message}`);
60
+ }
61
+ if (error.response && error.response.data) {
62
+ const parsedError = parseStateZeroError(error.response.data);
63
+ if (Error.captureStackTrace) {
64
+ Error.captureStackTrace(parsedError, sendNotification);
65
+ }
66
+ throw parsedError;
67
+ }
68
+ else if (error.request) {
69
+ throw new Error(`Send Notification failed: No response received from server.`);
70
+ }
71
+ else {
72
+ throw new Error(`Send Notification failed: ${error.message}`);
73
+ }
74
+ }
75
+ }
76
+ export default sendNotification;
77
+ sendNotification.actionName = 'send_notification';
78
+ sendNotification.title = 'Send Notification';
79
+ sendNotification.app = 'django_app';
80
+ sendNotification.permissions = ['CanSendNotifications'];
81
+ sendNotification.configKey = 'default';
@@ -0,0 +1,175 @@
1
+ {
2
+ "action_name": "send_notification",
3
+ "app": "django_app",
4
+ "title": "Send Notification",
5
+ "docstring": "Send notifications to multiple recipients",
6
+ "class_name": "SendNotification",
7
+ "input_properties": {
8
+ "message": {
9
+ "type": "string",
10
+ "title": "Message",
11
+ "required": true,
12
+ "description": "Notification message to send",
13
+ "nullable": false,
14
+ "format": null,
15
+ "max_length": 500,
16
+ "choices": null,
17
+ "default": null,
18
+ "validators": [],
19
+ "max_digits": null,
20
+ "decimal_places": null,
21
+ "read_only": false,
22
+ "ref": null
23
+ },
24
+ "recipients": {
25
+ "type": "array",
26
+ "title": "Recipients",
27
+ "required": true,
28
+ "description": "List of email addresses to notify",
29
+ "nullable": false,
30
+ "format": null,
31
+ "max_length": null,
32
+ "choices": null,
33
+ "default": null,
34
+ "validators": [],
35
+ "max_digits": null,
36
+ "decimal_places": null,
37
+ "read_only": false,
38
+ "ref": null
39
+ },
40
+ "priority": {
41
+ "type": "string",
42
+ "title": "Priority",
43
+ "required": false,
44
+ "description": "Notification priority level",
45
+ "nullable": false,
46
+ "format": null,
47
+ "max_length": null,
48
+ "choices": {
49
+ "low": "Low",
50
+ "high": "High"
51
+ },
52
+ "default": "low",
53
+ "validators": [],
54
+ "max_digits": null,
55
+ "decimal_places": null,
56
+ "read_only": false,
57
+ "ref": null
58
+ }
59
+ },
60
+ "response_properties": {
61
+ "success": {
62
+ "type": "boolean",
63
+ "title": "Success",
64
+ "required": true,
65
+ "description": null,
66
+ "nullable": false,
67
+ "format": null,
68
+ "max_length": null,
69
+ "choices": null,
70
+ "default": null,
71
+ "validators": [],
72
+ "max_digits": null,
73
+ "decimal_places": null,
74
+ "read_only": false,
75
+ "ref": null
76
+ },
77
+ "message_id": {
78
+ "type": "string",
79
+ "title": "Message id",
80
+ "required": true,
81
+ "description": null,
82
+ "nullable": false,
83
+ "format": null,
84
+ "max_length": null,
85
+ "choices": null,
86
+ "default": null,
87
+ "validators": [],
88
+ "max_digits": null,
89
+ "decimal_places": null,
90
+ "read_only": false,
91
+ "ref": null
92
+ },
93
+ "sent_to": {
94
+ "type": "integer",
95
+ "title": "Sent to",
96
+ "required": true,
97
+ "description": null,
98
+ "nullable": false,
99
+ "format": null,
100
+ "max_length": null,
101
+ "choices": null,
102
+ "default": null,
103
+ "validators": [],
104
+ "max_digits": null,
105
+ "decimal_places": null,
106
+ "read_only": false,
107
+ "ref": null
108
+ },
109
+ "queued_at": {
110
+ "type": "string",
111
+ "title": "Queued at",
112
+ "required": true,
113
+ "description": null,
114
+ "nullable": false,
115
+ "format": "date-time",
116
+ "max_length": null,
117
+ "choices": null,
118
+ "default": null,
119
+ "validators": [],
120
+ "max_digits": null,
121
+ "decimal_places": null,
122
+ "read_only": false,
123
+ "ref": null
124
+ }
125
+ },
126
+ "relationships": {},
127
+ "permissions": [
128
+ "CanSendNotifications"
129
+ ],
130
+ "display": {
131
+ "display_title": "Send Notifications",
132
+ "display_description": "Send notifications to multiple recipients with priority control",
133
+ "field_groups": [
134
+ {
135
+ "display_title": "Message Content",
136
+ "display_description": "The notification message and priority level",
137
+ "field_names": [
138
+ "message",
139
+ "priority"
140
+ ]
141
+ },
142
+ {
143
+ "display_title": "Recipients",
144
+ "display_description": "Email addresses to send notifications to",
145
+ "field_names": [
146
+ "recipients"
147
+ ]
148
+ }
149
+ ],
150
+ "field_display_configs": [
151
+ {
152
+ "field_name": "message",
153
+ "display_component": "TextArea",
154
+ "filter_queryset": null,
155
+ "display_help_text": "Enter your notification message here (max 500 characters)",
156
+ "extra": null
157
+ },
158
+ {
159
+ "field_name": "priority",
160
+ "display_component": "RadioGroup",
161
+ "filter_queryset": null,
162
+ "display_help_text": "High priority notifications are processed first",
163
+ "extra": null
164
+ },
165
+ {
166
+ "field_name": "recipients",
167
+ "display_component": "EmailListInput",
168
+ "filter_queryset": null,
169
+ "display_help_text": "Add one or more email addresses",
170
+ "extra": null
171
+ }
172
+ ],
173
+ "extra": null
174
+ }
175
+ }
@@ -0,0 +1 @@
1
+ export * from "./django_app/index.js";
@@ -0,0 +1 @@
1
+ export * from './django_app/index.js';
@@ -0,0 +1 @@
1
+ export * from "./default/index.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * This file was auto-generated. Do not make direct changes to the file.
3
+ * Re-exports from the 'default' backend for backwards compatibility.
4
+ */
5
+ export * from './default/index.js';
@@ -7,14 +7,7 @@
7
7
  * @returns {string[]} Array of dot-notation paths, e.g. ['author.id','createdAt.year']
8
8
  */
9
9
  export function getRequiredFields(queryBuild: Object, ModelClass: Class): string[];
10
- /**
11
- * Pick out only the required fields from a (possibly nested) model object.
12
- *
13
- * @param {string[]} requiredPaths – e.g. ['id','related.name','related.age']
14
- * @param {Object} instance – e.g. { id: 3, related: { name: 'bob', age: 12, foo: 'bar' } }
15
- * @returns {Object} – e.g. { id: 3, related: { name: 'bob', age: 12 } }
16
- */
17
- export function pickRequiredFields(requiredPaths: string[], instance: Object): Object;
10
+ export function pickRequiredFields(requiredPaths: any, instance: any): {};
18
11
  /**
19
12
  * Filter and order a collection of data objects according to a QuerySet's AST.
20
13
  * This combines getRequiredFields, pickRequiredFields, and processQuery in one function.
@@ -99,6 +99,30 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
99
99
  const relationship = currentModel.relationshipFields.get(part);
100
100
  const relatedModel = relationship.ModelClass();
101
101
  const relationshipType = relationship.relationshipType;
102
+ // If this is not the last part and it's M2M, recursively process the remaining path
103
+ if (!isLastPart && relationshipType === 'many-to-many') {
104
+ // Build the remaining path including any lookup operators
105
+ const remainingFieldParts = fieldParts.slice(i + 1);
106
+ let fullRemainingPath = remainingFieldParts.join('__');
107
+ if (lookupChain.length > 0) {
108
+ fullRemainingPath += '__' + lookupChain.join('__');
109
+ }
110
+ else if (lookup) {
111
+ fullRemainingPath += '__' + lookup;
112
+ }
113
+ // Recursively process the remaining path with the related model
114
+ const innerResult = processFieldPath(fullRemainingPath, value, relatedModel, options);
115
+ // Build the required path for data picking (full dot-notation path)
116
+ const innerRequiredPath = innerResult.requiredPath || innerResult.field;
117
+ const requiredPath = `${part}.${innerRequiredPath}`;
118
+ // Wrap the inner result in $elemMatch for this M2M field
119
+ return {
120
+ field: part,
121
+ operator: { $elemMatch: { [innerResult.field]: innerResult.operator } },
122
+ isM2M: true,
123
+ requiredPath // Full path for data picking
124
+ };
125
+ }
102
126
  // Add this relationship field to the path
103
127
  processedPath.push(part);
104
128
  // If this is not the last part, update the current model to the related model
@@ -168,6 +192,10 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
168
192
  if (normalizedValue && typeof normalizedValue === 'object' && 'pk' in normalizedValue) {
169
193
  raw = normalizedValue.pk;
170
194
  }
195
+ // For M2M fields, use $elemMatch to check if any element's pk matches
196
+ if (isM2M) {
197
+ return { field: finalPath, operator: { $elemMatch: { pk: { $eq: raw } } }, isM2M: true };
198
+ }
171
199
  return { field: finalPath, operator: { $eq: raw } };
172
200
  }
173
201
  // Default to direct equality
@@ -217,18 +245,19 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
217
245
  // For relationship fields with lookups, we need special handling
218
246
  if (lookup === 'isnull') {
219
247
  if (isM2M) {
220
- // For M2M fields, isnull=True means null OR empty array, but not undefined because
221
- // that can mean the field was not fetched so we can't be sure
248
+ // For M2M fields, isnull=True means null OR empty array
222
249
  // isnull=False means has at least one item
223
- // Use a custom function since sift doesn't support $or inside field operators
250
+ // Use document-level $where to check the array itself (not iterate elements)
251
+ const m2mField = field;
252
+ const checkEmpty = value;
224
253
  return {
225
- field,
226
- operator: {
227
- $where: function (fieldValue) {
228
- const isEmpty = fieldValue === null ||
229
- (Array.isArray(fieldValue) && fieldValue.length === 0);
230
- return value ? isEmpty : !isEmpty;
231
- }
254
+ field: '$where', // document-level operator
255
+ requiredPath: field, // need the M2M field itself for data picking
256
+ operator: function (doc) {
257
+ const fieldValue = doc[m2mField];
258
+ const isEmpty = fieldValue === null ||
259
+ (Array.isArray(fieldValue) && fieldValue.length === 0);
260
+ return checkEmpty ? isEmpty : !isEmpty;
232
261
  }
233
262
  };
234
263
  }
@@ -239,10 +268,17 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
239
268
  };
240
269
  }
241
270
  else if (lookup === 'in') {
271
+ // For M2M, check if any element's pk is in the provided array
272
+ if (isM2M) {
273
+ return { field, operator: { $elemMatch: { pk: { $in: value } } }, isM2M: true };
274
+ }
242
275
  return { field, operator: { $in: value } };
243
276
  }
244
277
  else {
245
278
  // Default handling for relationship fields
279
+ if (isM2M) {
280
+ return { field, operator: { $elemMatch: { pk: { $eq: value } } }, isM2M: true };
281
+ }
246
282
  return { field, operator: { $eq: value } };
247
283
  }
248
284
  }
@@ -545,10 +581,11 @@ function createFilterWithDateOperations(criteria, ModelClass) {
545
581
  function convertToSiftCriteria(conditions, ModelClass) {
546
582
  const result = {};
547
583
  const datePartFilters = new Map(); // Map to collect date part filters by field
584
+ const m2mConditions = new Map(); // Map to collect M2M $elemMatch conditions by field
548
585
  for (const [key, value] of Object.entries(conditions)) {
549
586
  try {
550
587
  const processedResult = processFieldPath(key, value, ModelClass);
551
- const { field, operator, isDatePart } = processedResult;
588
+ const { field, operator, isDatePart, isM2M } = processedResult;
552
589
  if (isDatePart) {
553
590
  // Handle date part operators separately
554
591
  if (!datePartFilters.has(field)) {
@@ -556,6 +593,13 @@ function convertToSiftCriteria(conditions, ModelClass) {
556
593
  }
557
594
  datePartFilters.get(field).push({ [field]: operator });
558
595
  }
596
+ else if (isM2M && operator.$elemMatch) {
597
+ // Collect M2M conditions to merge into single $elemMatch
598
+ if (!m2mConditions.has(field)) {
599
+ m2mConditions.set(field, []);
600
+ }
601
+ m2mConditions.get(field).push(operator.$elemMatch);
602
+ }
559
603
  else {
560
604
  // For regular operators, merge if we already have criteria for this field
561
605
  if (result[field]) {
@@ -570,6 +614,17 @@ function convertToSiftCriteria(conditions, ModelClass) {
570
614
  throw new Error(`Failed to process field '${key}': ${error.message}`);
571
615
  }
572
616
  }
617
+ // Merge M2M conditions: all conditions on same M2M field go into single $elemMatch
618
+ // so the SAME related object must match ALL conditions
619
+ for (const [field, elemMatchConditions] of m2mConditions.entries()) {
620
+ if (elemMatchConditions.length === 1) {
621
+ result[field] = { $elemMatch: elemMatchConditions[0] };
622
+ }
623
+ else {
624
+ // Multiple conditions - wrap in $and so same element must match all
625
+ result[field] = { $elemMatch: { $and: elemMatchConditions } };
626
+ }
627
+ }
573
628
  // If we have date part filters, combine them with the result
574
629
  if (datePartFilters.size > 0) {
575
630
  const andConditions = [];
@@ -663,6 +718,9 @@ function convertFilterNodeToSiftCriteria(filterNode, ModelClass) {
663
718
  return null;
664
719
  if (childCriteria.length === 1)
665
720
  return childCriteria[0];
721
+ // Chained filters use $and at top level - this gives ANY/ANY semantics for M2M
722
+ // (each $elemMatch can be satisfied by different elements)
723
+ // This matches Django's chained .filter() behavior
666
724
  return { $and: childCriteria };
667
725
  }
668
726
  // For compound OR nodes
@@ -863,12 +921,24 @@ export function getRequiredFields(queryBuild, ModelClass) {
863
921
  function addPath(rawKey) {
864
922
  try {
865
923
  // We pass `null` as the value, since we only care about .field
866
- const { field } = processFieldPath(rawKey, null, ModelClass);
867
- paths.add(field);
924
+ const { field, isM2M, requiredPath } = processFieldPath(rawKey, null, ModelClass);
925
+ // Use requiredPath if available (for M2M traversal), otherwise use field
926
+ // For M2M fields at the end of a path (no requiredPath), we need the pk field
927
+ let finalPath;
928
+ if (requiredPath) {
929
+ finalPath = requiredPath;
930
+ }
931
+ else if (isM2M) {
932
+ finalPath = `${field}.pk`;
933
+ }
934
+ else {
935
+ finalPath = field;
936
+ }
937
+ paths.add(finalPath);
868
938
  }
869
939
  catch (err) {
870
- // if a key doesnt map, warn and skip it
871
- console.warn(`getRequiredFields: couldnt process "${rawKey}": ${err.message}`);
940
+ // if a key doesn't map, warn and skip it
941
+ console.warn(`getRequiredFields: couldn't process "${rawKey}": ${err.message}`);
872
942
  }
873
943
  }
874
944
  // Recursively walk your filter AST
@@ -917,42 +987,48 @@ export function getRequiredFields(queryBuild, ModelClass) {
917
987
  * @param {Object} instance – e.g. { id: 3, related: { name: 'bob', age: 12, foo: 'bar' } }
918
988
  * @returns {Object} – e.g. { id: 3, related: { name: 'bob', age: 12 } }
919
989
  */
990
+ /**
991
+ * Recursively sets a value in a result object following a path.
992
+ * Handles arrays (M2M fields) by mapping over each element.
993
+ */
994
+ function setNestedValueRecursive(result, source, pathParts) {
995
+ if (source == null || pathParts.length === 0) {
996
+ return;
997
+ }
998
+ const [current, ...rest] = pathParts;
999
+ const sourceValue = source[current];
1000
+ if (sourceValue === undefined) {
1001
+ return;
1002
+ }
1003
+ if (rest.length === 0) {
1004
+ // Last part - set the value directly, keeping M2M as full objects
1005
+ result[current] = sourceValue;
1006
+ }
1007
+ else if (Array.isArray(sourceValue)) {
1008
+ // M2M array with nested path - recursively extract from each element
1009
+ if (!(current in result)) {
1010
+ result[current] = [];
1011
+ }
1012
+ sourceValue.forEach((item, idx) => {
1013
+ if (result[current][idx] === undefined) {
1014
+ result[current][idx] = {};
1015
+ }
1016
+ setNestedValueRecursive(result[current][idx], item, rest);
1017
+ });
1018
+ }
1019
+ else if (typeof sourceValue === 'object') {
1020
+ // Regular nested object (FK)
1021
+ if (!(current in result)) {
1022
+ result[current] = {};
1023
+ }
1024
+ setNestedValueRecursive(result[current], sourceValue, rest);
1025
+ }
1026
+ }
920
1027
  export function pickRequiredFields(requiredPaths, instance) {
921
1028
  const result = {};
922
1029
  requiredPaths.forEach(path => {
923
1030
  const parts = path.split('.');
924
- // Traverse the source instance to get the value
925
- let value = instance;
926
- for (const key of parts) {
927
- if (value == null || !(key in value)) {
928
- value = undefined;
929
- break;
930
- }
931
- value = value[key];
932
- }
933
- if (value === undefined) {
934
- // skip missing
935
- return;
936
- }
937
- // Convert M2M arrays from Model instances to PKs for comparison
938
- // This handles the case where the live representation returns [Model1, Model2]
939
- // but we need [pk1, pk2] for sift filtering
940
- if (Array.isArray(value) && value.length > 0 && value[0]?.pk !== undefined) {
941
- value = value.map(item => item.pk);
942
- }
943
- // Build nested structure in the result
944
- let cursor = result;
945
- parts.forEach((key, idx) => {
946
- if (idx === parts.length - 1) {
947
- cursor[key] = value;
948
- }
949
- else {
950
- if (!(key in cursor)) {
951
- cursor[key] = {};
952
- }
953
- cursor = cursor[key];
954
- }
955
- });
1031
+ setNestedValueRecursive(result, instance, parts);
956
1032
  });
957
1033
  return result;
958
1034
  }
@@ -83,7 +83,10 @@ export class DateParsingHelpers {
83
83
  try {
84
84
  // Handle ISO format (Django's default)
85
85
  if (!dateFormat || dateFormat === 'iso-8601') {
86
- return date.toISOString();
86
+ // For date-only fields, return just the date portion
87
+ return fieldFormat === 'date'
88
+ ? date.toISOString().slice(0, 10)
89
+ : date.toISOString();
87
90
  }
88
91
  // Handle supported Django formats
89
92
  const dateFnsFormat = this.SUPPORTED_FORMATS[dateFormat];
@@ -75,9 +75,13 @@ export const dateFieldSerializer = {
75
75
  return value;
76
76
  // If it's a Date object, serialize it using DateParsingHelpers
77
77
  if (value instanceof Date) {
78
- const { model, field } = context;
79
- if (model?.schema) {
80
- return DateParsingHelpers.serializeDate(value, field, model.schema);
78
+ const { model, field, fieldSchema } = context;
79
+ // Use model schema, or create minimal schema from fieldSchema (for actions)
80
+ const schema = model?.schema || (fieldSchema?.format && {
81
+ properties: { [field]: { format: fieldSchema.format } }
82
+ });
83
+ if (schema) {
84
+ return DateParsingHelpers.serializeDate(value, field, schema);
81
85
  }
82
86
  // Fallback if no schema context
83
87
  return value.toISOString();
@@ -214,7 +218,7 @@ export function serializeActionPayload(payload, inputProperties) {
214
218
  const typeSerializers = serializers[type];
215
219
  const serializer = typeSerializers?.[format];
216
220
  if (serializer) {
217
- serializedPayload[field] = serializer.toInternal(value, { field });
221
+ serializedPayload[field] = serializer.toInternal(value, { field, fieldSchema });
218
222
  }
219
223
  else {
220
224
  serializedPayload[field] = value;