@payyo/ptp-js 1.0.0-alpha.1 → 1.0.0-beta.2

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.
@@ -0,0 +1,95 @@
1
+ export declare function createForm(config: FormConfig): Form;
2
+
3
+ export declare type Field = {
4
+ iframe: HTMLIFrameElement;
5
+ name: string;
6
+ type: FieldType;
7
+ iFrameName: string;
8
+ container: HTMLElement;
9
+ title: string;
10
+ placeholder: string;
11
+ inputType: string;
12
+ isReady: boolean;
13
+ };
14
+
15
+ export declare type FieldConfig = {
16
+ fieldId: string;
17
+ fieldName: string;
18
+ fieldType: FieldType;
19
+ formId: string;
20
+ fieldNames: string[];
21
+ title: string;
22
+ placeholder: string;
23
+ inputType: string;
24
+ endpoint: string;
25
+ mode: Mode;
26
+ debug: boolean;
27
+ };
28
+
29
+ export declare type FieldEvent = {
30
+ fieldName: string;
31
+ params: any;
32
+ };
33
+
34
+ export declare type FieldParams = {
35
+ type: FieldType;
36
+ container: HTMLElement | string;
37
+ placeholder: string;
38
+ title: string;
39
+ inputType?: string;
40
+ };
41
+
42
+ export declare enum FieldType {
43
+ CardNumber = "cardNumber",
44
+ SecurityCode = "securityCode"
45
+ }
46
+
47
+ export declare type Form = {
48
+ init: (fieldsMap: Record<string, FieldParams>) => void;
49
+ onChange: (handler: (event: FieldEvent) => void) => void;
50
+ onReady: (handler: (event: FieldEvent) => void) => void;
51
+ onFocus: (handler: (event: FieldEvent) => void) => void;
52
+ onBlur: (handler: (event: FieldEvent) => void) => void;
53
+ focus: (field: string) => void;
54
+ validate: () => Promise<any>;
55
+ setStyle: (fieldName: string, selector: string, cssRules: Record<string, any>) => void;
56
+ submit: (params?: any) => Promise<any>;
57
+ destroy: () => void;
58
+ };
59
+
60
+ export declare type FormConfig = {
61
+ mode: Mode | string;
62
+ debug: boolean;
63
+ /** is used in `Mode.Test` mode only */
64
+ fieldUrl?: string;
65
+ };
66
+
67
+ export declare type Message = {
68
+ params: any;
69
+ type: string;
70
+ formId: string;
71
+ messageId: string | null;
72
+ response?: MessageResponse;
73
+ };
74
+
75
+ export declare type MessageResponse = {
76
+ success: boolean;
77
+ result?: null;
78
+ error?: string | null;
79
+ fields?: Record<string, boolean>;
80
+ error_details?: string;
81
+ };
82
+
83
+ export declare enum Mode {
84
+ Test = "test",
85
+ Live = "live"
86
+ }
87
+
88
+ export declare type ReplyCallback = (type: string, event: MessageResponse) => void;
89
+
90
+ export declare type SetStyleEvent = {
91
+ selector: string;
92
+ rules: string[];
93
+ };
94
+
95
+ export { }
@@ -3,12 +3,17 @@ var Mode = /* @__PURE__ */ ((Mode2) => {
3
3
  Mode2["Live"] = "live";
4
4
  return Mode2;
5
5
  })(Mode || {});
6
- function form(config) {
6
+ var FieldType = /* @__PURE__ */ ((FieldType2) => {
7
+ FieldType2["CardNumber"] = "cardNumber";
8
+ FieldType2["SecurityCode"] = "securityCode";
9
+ return FieldType2;
10
+ })(FieldType || {});
11
+ function createForm(config) {
7
12
  const fields = {};
8
13
  const fieldNames = [];
9
14
  const callbacks = {};
10
15
  const formId = generateRandomId();
11
- let fieldUrl = config.fieldUrl && config.mode === Mode.Test ? config.fieldUrl : "https://cdn.ptp.payyo.ch/ptp-js/1.0.0-alpha.1/field.html";
16
+ let fieldUrl = config.fieldUrl && config.mode === Mode.Test ? config.fieldUrl : "https://cdn.ptp.payyo.ch/ptp-js/1.0.0-beta.2/field.html";
12
17
  const EVENT_ON_CHANGE = "change";
13
18
  const EVENT_ON_READY = "ready";
14
19
  const EVENT_ON_FOCUS = "focus";
@@ -32,7 +37,8 @@ function form(config) {
32
37
  // @ts-ignore
33
38
  src: `${fieldUrl}?${new URLSearchParams({
34
39
  form_id: formId,
35
- field_name: field.fieldName,
40
+ field_name: field.name,
41
+ field_type: field.type,
36
42
  field_names: fieldNames,
37
43
  main_field_name: mainFieldName || "",
38
44
  title: field.title,
@@ -62,18 +68,18 @@ function form(config) {
62
68
  function postMessage(type, params = {}) {
63
69
  const message = { params, type, formId, messageId: null };
64
70
  return new Promise((resolve) => {
65
- const messageId = type + ":" + generateRandomId();
71
+ const messageId = `${type}:${generateRandomId()}`;
66
72
  callbacks[messageId] = resolve;
67
73
  message.messageId = messageId;
68
74
  log("send message", message);
69
75
  if (mainFieldName) {
70
- window.frames[fields[mainFieldName].iFrameName].postMessage(message, "*");
76
+ const mainIFrameName = fields[mainFieldName].iFrameName;
77
+ window.frames[mainIFrameName].postMessage(message, "*");
71
78
  }
72
79
  });
73
80
  }
74
81
  const handlers = {};
75
- function emitEvent(..._) {
76
- const args = Array.prototype.slice.call(arguments);
82
+ function emitEvent(...args) {
77
83
  const eventName = args.shift();
78
84
  const handler = handlers[eventName];
79
85
  if (handler) {
@@ -91,23 +97,26 @@ function form(config) {
91
97
  for (let [, field] of Object.entries(fields)) {
92
98
  const iframe = window.frames[field.iFrameName];
93
99
  if (iframe) {
94
- iframe.postMessage({ "type": "checkFocus" }, "*");
100
+ iframe.postMessage({ type: "checkFocus" }, "*");
95
101
  }
96
102
  }
97
103
  }
98
- const form2 = {
104
+ const form = {
99
105
  init: function(fieldsMap) {
100
106
  for (let fieldName in fieldsMap) {
101
107
  if (!fieldsMap.hasOwnProperty(fieldName)) {
102
108
  return;
103
109
  }
104
110
  let field = fieldsMap[fieldName];
105
- let fieldContainer = field.container;
106
- if (typeof fieldContainer === "string") {
107
- fieldContainer = document.getElementById(fieldContainer);
111
+ let fieldContainer = null;
112
+ if (typeof field.container === "string") {
113
+ fieldContainer = document.getElementById(field.container);
114
+ } else {
115
+ fieldContainer = field.container;
108
116
  }
109
117
  fields[fieldName] = {
110
- fieldName,
118
+ name: fieldName,
119
+ type: field.type,
111
120
  iFrameName: "ptp-field-iframe-" + formId + "-" + fieldName,
112
121
  container: fieldContainer,
113
122
  title: field.title || "",
@@ -221,16 +230,18 @@ function form(config) {
221
230
  const messageId = event.messageId, callback = callbacks[messageId || ""] ?? void 0;
222
231
  if (messageId && callback !== void 0) {
223
232
  delete callbacks[messageId];
224
- callback.apply(form2, [event.response]);
233
+ callback.apply(form, [event.response]);
225
234
  } else {
226
235
  const handler = handlers2[event.type];
227
236
  if (handler) {
228
- handler.apply(form2, [event.response]);
237
+ handler.apply(form, [event.response]);
229
238
  }
230
239
  }
231
240
  }
232
- return form2;
241
+ return form;
233
242
  }
234
243
  export {
235
- form as default
244
+ FieldType,
245
+ Mode,
246
+ createForm
236
247
  };
package/dist/index.js ADDED
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ var Mode = /* @__PURE__ */ ((Mode2) => {
4
+ Mode2["Test"] = "test";
5
+ Mode2["Live"] = "live";
6
+ return Mode2;
7
+ })(Mode || {});
8
+ var FieldType = /* @__PURE__ */ ((FieldType2) => {
9
+ FieldType2["CardNumber"] = "cardNumber";
10
+ FieldType2["SecurityCode"] = "securityCode";
11
+ return FieldType2;
12
+ })(FieldType || {});
13
+ function createForm(config) {
14
+ const fields = {};
15
+ const fieldNames = [];
16
+ const callbacks = {};
17
+ const formId = generateRandomId();
18
+ let fieldUrl = config.fieldUrl && config.mode === Mode.Test ? config.fieldUrl : "https://cdn.ptp.payyo.ch/ptp-js/1.0.0-beta.2/field.html";
19
+ const EVENT_ON_CHANGE = "change";
20
+ const EVENT_ON_READY = "ready";
21
+ const EVENT_ON_FOCUS = "focus";
22
+ const EVENT_ON_BLUR = "blur";
23
+ let mainFieldName = null;
24
+ function generateRandomId() {
25
+ return Math.random().toString(36).substring(2, 9);
26
+ }
27
+ function log(...args) {
28
+ if (config.debug && window.console) {
29
+ args.unshift("[PTP FORM]");
30
+ console.log.apply(console, args);
31
+ }
32
+ }
33
+ function createIframe(field) {
34
+ const placeholder = field.container, iFrameName = field.iFrameName, attributes = {
35
+ id: iFrameName,
36
+ name: iFrameName,
37
+ title: field.title,
38
+ class: iFrameName,
39
+ // @ts-ignore
40
+ src: `${fieldUrl}?${new URLSearchParams({
41
+ form_id: formId,
42
+ field_name: field.name,
43
+ field_type: field.type,
44
+ field_names: fieldNames,
45
+ main_field_name: mainFieldName || "",
46
+ title: field.title,
47
+ placeholder: field.placeholder,
48
+ input_type: field.inputType,
49
+ mode: config.mode,
50
+ debug: config.debug ? "1" : "0"
51
+ }).toString()}`,
52
+ frameborder: "0",
53
+ scrolling: "no",
54
+ style: "width: 100%; height: 100%; border: none;"
55
+ }, iframe = document.createElement("iframe");
56
+ Object.keys(attributes).forEach(function(key) {
57
+ iframe.setAttribute(key, attributes[key]);
58
+ });
59
+ while (placeholder.lastChild && placeholder.hasChildNodes()) {
60
+ placeholder.removeChild(placeholder.lastChild);
61
+ }
62
+ placeholder.appendChild(iframe);
63
+ field.iframe = iframe;
64
+ }
65
+ function createRemainingIframes() {
66
+ for (let i = 1; i < fieldNames.length; i++) {
67
+ createIframe(fields[fieldNames[i]]);
68
+ }
69
+ }
70
+ function postMessage(type, params = {}) {
71
+ const message = { params, type, formId, messageId: null };
72
+ return new Promise((resolve) => {
73
+ const messageId = `${type}:${generateRandomId()}`;
74
+ callbacks[messageId] = resolve;
75
+ message.messageId = messageId;
76
+ log("send message", message);
77
+ if (mainFieldName) {
78
+ const mainIFrameName = fields[mainFieldName].iFrameName;
79
+ window.frames[mainIFrameName].postMessage(message, "*");
80
+ }
81
+ });
82
+ }
83
+ const handlers = {};
84
+ function emitEvent(...args) {
85
+ const eventName = args.shift();
86
+ const handler = handlers[eventName];
87
+ if (handler) {
88
+ log(`emit event "${eventName}"`, args);
89
+ handler.apply(null, args);
90
+ } else {
91
+ log(`no handler for event "${eventName}"`, args);
92
+ }
93
+ }
94
+ window.addEventListener("message", receiveMessage, false);
95
+ function addOnTouchedEventListener() {
96
+ window.addEventListener("touchend", checkFocus, true);
97
+ }
98
+ function checkFocus() {
99
+ for (let [, field] of Object.entries(fields)) {
100
+ const iframe = window.frames[field.iFrameName];
101
+ if (iframe) {
102
+ iframe.postMessage({ type: "checkFocus" }, "*");
103
+ }
104
+ }
105
+ }
106
+ const form = {
107
+ init: function(fieldsMap) {
108
+ for (let fieldName in fieldsMap) {
109
+ if (!fieldsMap.hasOwnProperty(fieldName)) {
110
+ return;
111
+ }
112
+ let field = fieldsMap[fieldName];
113
+ let fieldContainer = null;
114
+ if (typeof field.container === "string") {
115
+ fieldContainer = document.getElementById(field.container);
116
+ } else {
117
+ fieldContainer = field.container;
118
+ }
119
+ fields[fieldName] = {
120
+ name: fieldName,
121
+ type: field.type,
122
+ iFrameName: "ptp-field-iframe-" + formId + "-" + fieldName,
123
+ container: fieldContainer,
124
+ title: field.title || "",
125
+ placeholder: field.placeholder || "",
126
+ inputType: field.inputType || "text",
127
+ isReady: false
128
+ };
129
+ fieldNames.push(fieldName);
130
+ }
131
+ mainFieldName = fieldNames[0];
132
+ createIframe(fields[mainFieldName]);
133
+ },
134
+ onChange(handler) {
135
+ handlers[EVENT_ON_CHANGE] = handler;
136
+ },
137
+ onReady(handler) {
138
+ handlers[EVENT_ON_READY] = handler;
139
+ },
140
+ onFocus(handler) {
141
+ handlers[EVENT_ON_FOCUS] = handler;
142
+ },
143
+ onBlur(handler) {
144
+ handlers[EVENT_ON_BLUR] = handler;
145
+ },
146
+ validate() {
147
+ return postMessage("validateRequest");
148
+ },
149
+ submit(params) {
150
+ return postMessage("submitRequest", {
151
+ params: params || {},
152
+ browserDetails: {
153
+ browserUserAgent: navigator.userAgent,
154
+ browserJavaEnabled: navigator.javaEnabled(),
155
+ browserLanguage: navigator.language,
156
+ browserColorDepth: screen.colorDepth,
157
+ browserScreenHeight: screen.height,
158
+ browserScreenWidth: screen.width,
159
+ browserTZ: (/* @__PURE__ */ new Date()).getTimezoneOffset()
160
+ }
161
+ }).then((res) => {
162
+ if (res.success) {
163
+ return Promise.resolve(res.result);
164
+ }
165
+ return Promise.reject({
166
+ error: res.error,
167
+ error_details: res.error_details
168
+ });
169
+ });
170
+ },
171
+ focus(field) {
172
+ const f = fields[field] || null;
173
+ if (f) {
174
+ window.frames[f.iFrameName].focus();
175
+ }
176
+ },
177
+ setStyle: function(fieldName, selector, cssRules) {
178
+ if (!fields[fieldName]) {
179
+ return;
180
+ }
181
+ window.frames[fields[fieldName].iFrameName].postMessage({
182
+ type: "setStyle",
183
+ params: {
184
+ selector,
185
+ rules: cssRules
186
+ }
187
+ }, "*");
188
+ },
189
+ destroy: function() {
190
+ var _a;
191
+ window.removeEventListener("message", receiveMessage, false);
192
+ window.addEventListener("touchend", checkFocus, true);
193
+ for (let fieldName in fields) {
194
+ let iframe = document.getElementById(fields[fieldName].iFrameName);
195
+ if (iframe) {
196
+ (_a = iframe.parentNode) == null ? void 0 : _a.removeChild(iframe);
197
+ }
198
+ }
199
+ }
200
+ };
201
+ function receiveMessage(message) {
202
+ const event = message.data;
203
+ if (formId !== event.formId) {
204
+ return;
205
+ }
206
+ log(`message received of type "${event.type}"`, event);
207
+ const handlers2 = {
208
+ fieldReady: (event2) => {
209
+ if (fields[event2.fieldName] === void 0) {
210
+ return;
211
+ }
212
+ fields[event2.fieldName].isReady = true;
213
+ if (event2.fieldName === mainFieldName) {
214
+ createRemainingIframes();
215
+ }
216
+ const areAllReady = Object.keys(fields).map((key) => fields[key].isReady).reduce((a, b) => a && b, true);
217
+ if (areAllReady) {
218
+ addOnTouchedEventListener();
219
+ emitEvent(EVENT_ON_READY);
220
+ }
221
+ },
222
+ fieldChanged: function(event2) {
223
+ emitEvent(EVENT_ON_CHANGE, event2);
224
+ },
225
+ fieldFocused: function(event2) {
226
+ emitEvent(EVENT_ON_FOCUS, event2);
227
+ },
228
+ fieldBlurred: function(event2) {
229
+ emitEvent(EVENT_ON_BLUR, event2);
230
+ }
231
+ };
232
+ const messageId = event.messageId, callback = callbacks[messageId || ""] ?? void 0;
233
+ if (messageId && callback !== void 0) {
234
+ delete callbacks[messageId];
235
+ callback.apply(form, [event.response]);
236
+ } else {
237
+ const handler = handlers2[event.type];
238
+ if (handler) {
239
+ handler.apply(form, [event.response]);
240
+ }
241
+ }
242
+ }
243
+ return form;
244
+ }
245
+ exports.FieldType = FieldType;
246
+ exports.Mode = Mode;
247
+ exports.createForm = createForm;
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@payyo/ptp-js",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "description": "JS library for Payyo Tokenization service (PTP)",
5
5
  "private": false,
6
6
  "author": "developers@payyo.ch",
7
7
  "license": "MIT",
8
- "main": "webpack.config.js",
9
8
  "repository": {
10
9
  "type": "git",
11
10
  "url": "git+ssh://git@bitbucket.org/payyoag/ptp-js.git"
@@ -15,8 +14,15 @@
15
14
  },
16
15
  "homepage": "https://bitbucket.org/payyoag/ptp-js#readme",
17
16
  "type": "module",
17
+ "main": "./dist/index.js",
18
+ "module": "./dist/index.es.js",
19
+ "types": "./dist/index.d.ts",
18
20
  "exports": {
19
- ".": "./dist/form.js"
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.es.js",
24
+ "require": "./dist/index.js"
25
+ }
20
26
  },
21
27
  "scripts": {
22
28
  "dev": "vite",
@@ -25,16 +31,21 @@
25
31
  "build:form": "tsc && MODULE_NAME=form vite build --config vite.config.build.js",
26
32
  "build:dev": "npm run build:dev:field",
27
33
  "build:dev:field": "tsc && MODULE_NAME=field vite build --config vite.config.build.js --mode=development",
28
- "preview": "vite preview"
34
+ "preview": "vite preview",
35
+ "test": "vitest run"
29
36
  },
30
37
  "devDependencies": {
38
+ "@fontsource/open-sans": "^5.2.6",
39
+ "@microsoft/api-extractor": "^7.52.8",
31
40
  "@types/cleave.js": "^1.4.12",
32
41
  "@types/node": "^20.12.7",
33
42
  "cleave.js": "^1.6.0",
34
43
  "sass": "^1.75.0",
35
44
  "typescript": "^5.2.2",
36
- "vite": "^5.2.0",
37
- "vite-plugin-singlefile": "^2.0.1"
45
+ "unplugin-dts": "^1.0.0-beta.0",
46
+ "vite": "^6.3.5",
47
+ "vite-plugin-singlefile": "^2.0.1",
48
+ "vitest": "^3.2.3"
38
49
  },
39
50
  "files": [
40
51
  "src/form",
package/src/form/form.ts CHANGED
@@ -1,7 +1,7 @@
1
- import {Field, FieldEvent, Form, FormConfig, Message, MessageResponse, Mode, SetStyleEvent} from "../types";
1
+ import {Field, FieldEvent, Form, FormConfig, Message, MessageResponse, Mode, SetStyleEvent} from "../types.ts";
2
2
 
3
- export default function (config: FormConfig): Form {
4
- const fields: Record<string, any> = {};
3
+ export function createForm(config: FormConfig): Form {
4
+ const fields: Record<string, Field> = {};
5
5
  const fieldNames: string[] = [];
6
6
  const callbacks: Record<string, Function> = {};
7
7
  const formId = generateRandomId();
@@ -37,7 +37,8 @@ export default function (config: FormConfig): Form {
37
37
  // @ts-ignore
38
38
  src: `${fieldUrl}?${(new URLSearchParams({
39
39
  form_id: formId,
40
- field_name: field.fieldName,
40
+ field_name: field.name,
41
+ field_type: field.type,
41
42
  field_names: fieldNames,
42
43
  main_field_name: mainFieldName || '',
43
44
  title: field.title,
@@ -46,8 +47,8 @@ export default function (config: FormConfig): Form {
46
47
  mode: config.mode,
47
48
  debug: config.debug ? '1' : '0',
48
49
  })).toString()}`,
49
- frameborder: "0",
50
- scrolling: "no",
50
+ frameborder: '0',
51
+ scrolling: 'no',
51
52
  style: 'width: 100%; height: 100%; border: none;',
52
53
  },
53
54
  iframe = document.createElement('iframe');
@@ -74,21 +75,22 @@ export default function (config: FormConfig): Form {
74
75
  const message: Message = {params: params, type: type, formId: formId, messageId: null};
75
76
 
76
77
  return new Promise((resolve) => {
77
- const messageId = type + ":" + generateRandomId();
78
+ const messageId = `${type}:${generateRandomId()}`;
78
79
  callbacks[messageId] = resolve;
79
80
  message.messageId = messageId;
80
81
 
81
82
  log('send message', message);
82
83
  if (mainFieldName) {
83
- window.frames[fields[mainFieldName].iFrameName].postMessage(message, '*');
84
+ const mainIFrameName = fields[mainFieldName].iFrameName;
85
+ // @ts-ignore
86
+ window.frames[mainIFrameName].postMessage(message, '*');
84
87
  }
85
88
  });
86
89
  }
87
90
 
88
91
  const handlers: Record<string, Function> = {};
89
92
 
90
- function emitEvent(..._: any[]) {
91
- const args = Array.prototype.slice.call(arguments);
93
+ function emitEvent(...args: any[]) {
92
94
  const eventName = args.shift();
93
95
  const handler = handlers[eventName];
94
96
  if (handler) {
@@ -107,9 +109,10 @@ export default function (config: FormConfig): Form {
107
109
 
108
110
  function checkFocus() {
109
111
  for (let [, field] of Object.entries(fields)) {
112
+ // @ts-ignore
110
113
  const iframe = window.frames[field.iFrameName];
111
114
  if (iframe) {
112
- iframe.postMessage({'type': 'checkFocus'}, '*');
115
+ iframe.postMessage({type: 'checkFocus'}, '*');
113
116
  }
114
117
  }
115
118
  }
@@ -122,14 +125,17 @@ export default function (config: FormConfig): Form {
122
125
  }
123
126
 
124
127
  let field = fieldsMap[fieldName];
125
- let fieldContainer = field.container;
128
+ let fieldContainer = null;
126
129
 
127
- if (typeof fieldContainer === 'string') {
128
- fieldContainer = document.getElementById(fieldContainer);
130
+ if (typeof field.container === 'string') {
131
+ fieldContainer = document.getElementById(field.container);
132
+ } else {
133
+ fieldContainer = field.container as HTMLElement;
129
134
  }
130
135
 
131
- fields[fieldName] = {
132
- fieldName: fieldName,
136
+ fields[fieldName] = <Field>{
137
+ name: fieldName,
138
+ type: field.type,
133
139
  iFrameName: 'ptp-field-iframe-' + formId + '-' + fieldName,
134
140
  container: fieldContainer,
135
141
  title: field.title || '',
@@ -186,6 +192,7 @@ export default function (config: FormConfig): Form {
186
192
  focus(field) {
187
193
  const f = fields[field] || null;
188
194
  if (f) {
195
+ // @ts-ignore
189
196
  window.frames[f.iFrameName].focus();
190
197
  }
191
198
  },
@@ -194,6 +201,7 @@ export default function (config: FormConfig): Form {
194
201
  return;
195
202
  }
196
203
 
204
+ // @ts-ignore
197
205
  window.frames[fields[fieldName].iFrameName].postMessage({
198
206
  type: 'setStyle',
199
207
  params: {
@@ -268,4 +276,4 @@ export default function (config: FormConfig): Form {
268
276
  }
269
277
 
270
278
  return form;
271
- };
279
+ }
@@ -0,0 +1,2 @@
1
+ export * from '../types.ts';
2
+ export * from './form.ts';