@agnostack/next-shopify 2.1.1-alpha.27 → 2.1.1-alpha.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.1.1-alpha.28](https://github.com/agnostack/next-shopify/compare/v2.1.1-alpha.27...v2.1.1-alpha.28) (2024-11-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* debug ([63160c1](https://github.com/agnostack/next-shopify/commit/63160c13b9c1fd1985f7a4ebf294b6c5f6d94063))
|
|
7
|
+
|
|
1
8
|
## [2.1.1-alpha.27](https://github.com/agnostack/next-shopify/compare/v2.1.1-alpha.26...v2.1.1-alpha.27) (2024-11-07)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protect.d.ts","sourceRoot":"","sources":["../../../../src/lib/handlers/ssr/protect.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"protect.d.ts","sourceRoot":"","sources":["../../../../src/lib/handlers/ssr/protect.js"],"names":[],"mappings":"AA4CO;;;;;;;;;;;;;GAqJN"}
|
|
@@ -27,53 +27,32 @@ exports.protectRoute = void 0;
|
|
|
27
27
|
const chalk_1 = __importDefault(require("chalk"));
|
|
28
28
|
const utils_1 = require("../../utils");
|
|
29
29
|
const shared_1 = require("../../../shared");
|
|
30
|
+
const processRedirect = (url, params, permanent = false) => {
|
|
31
|
+
// TODO: explore objectToQuerystring
|
|
32
|
+
const queryString = new URLSearchParams((0, shared_1.ensureObject)(params)).toString();
|
|
33
|
+
console.info(`>>> ~ processRedirect`, {
|
|
34
|
+
queryString,
|
|
35
|
+
params,
|
|
36
|
+
url,
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
redirect: {
|
|
40
|
+
destination: `${url}${(0, shared_1.stringNotEmpty)(queryString) ? `?${queryString}` : ''}`,
|
|
41
|
+
permanent,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
};
|
|
30
45
|
const protectRoute = (serverRuntimeConfig, _props) => {
|
|
31
46
|
const { PAGE_PATHS, API_PATHS, APP_CONFIG, PAYMENT_CONFIG } = serverRuntimeConfig;
|
|
32
47
|
const isEmbedded = APP_CONFIG.isEmbeddedApp;
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
console.info(`>>> ~ processExitRedirect queryString`, {
|
|
36
|
-
queryString,
|
|
37
|
-
query,
|
|
38
|
-
shop,
|
|
39
|
-
url,
|
|
40
|
-
});
|
|
41
|
-
// TODO determine the correct way to refresh a session token via AppBridge version 3
|
|
42
|
-
return {
|
|
43
|
-
redirect: {
|
|
44
|
-
destination: `${PAGE_PATHS[shared_1.PAGE_ROUTE_NAMES.EXIT]}?${queryString}`,
|
|
45
|
-
permanent: false,
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
const processErrorRedirect = ({ shop, query, code }) => {
|
|
50
|
-
const queryString = new URLSearchParams(Object.assign(Object.assign({}, query), { shop,
|
|
51
|
-
code, type: 'protectRoute' })).toString();
|
|
52
|
-
console.info(`>>> ~ processErrorRedirect queryString`, {
|
|
53
|
-
queryString,
|
|
54
|
-
query,
|
|
55
|
-
shop,
|
|
56
|
-
code,
|
|
57
|
-
});
|
|
58
|
-
return {
|
|
59
|
-
redirect: {
|
|
60
|
-
destination: `${PAGE_PATHS[shared_1.PAGE_ROUTE_NAMES.ERROR]}?${queryString}`,
|
|
61
|
-
permanent: false,
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
};
|
|
48
|
+
const exitPath = PAGE_PATHS[shared_1.PAGE_ROUTE_NAMES.EXIT];
|
|
49
|
+
const errorPath = PAGE_PATHS[shared_1.PAGE_ROUTE_NAMES.ERROR];
|
|
65
50
|
return ({ req, res, query, locale, resolvedUrl, // NOTE: this comes in from nextJS
|
|
66
51
|
} = {}, data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
67
52
|
var _a, _b, _c, _d;
|
|
68
53
|
const [shop] = (0, shared_1.ensureArray)((_a = query === null || query === void 0 ? void 0 : query.shop) !== null && _a !== void 0 ? _a : (_b = req.query) === null || _b === void 0 ? void 0 : _b.shop);
|
|
69
54
|
if ((0, shared_1.stringEmpty)(shop)) {
|
|
70
|
-
return
|
|
71
|
-
// return {
|
|
72
|
-
// redirect: {
|
|
73
|
-
// destination: `${PAGE_PATHS[PAGE_ROUTE_NAMES.ERROR]}?code=no_shop_provided&type=protectRoute`,
|
|
74
|
-
// permanent: false,
|
|
75
|
-
// },
|
|
76
|
-
// }
|
|
55
|
+
return processRedirect(errorPath, Object.assign(Object.assign({}, query), { code: 'no_shop_provided' }));
|
|
77
56
|
}
|
|
78
57
|
const props = (0, shared_1.cleanObject)(Object.assign(Object.assign({ locale }, _props), data), false, shared_1.stringEmptyOnly);
|
|
79
58
|
// NOTE: this should handle running in next to test non-shopify functionality
|
|
@@ -102,11 +81,7 @@ const protectRoute = (serverRuntimeConfig, _props) => {
|
|
|
102
81
|
const [resolvedPath, _resolvedParams] = (0, shared_1.ensureString)(resolvedUrl).split('?');
|
|
103
82
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
104
83
|
const _e = (0, shared_1.querystringToObject)(_resolvedParams), _f = shared_1.SESSION_TOKEN_PARAM, _oldToken = _e[_f], resolvedParams = __rest(_e, [typeof _f === "symbol" ? _f : _f + ""]);
|
|
105
|
-
return
|
|
106
|
-
shop,
|
|
107
|
-
query,
|
|
108
|
-
url: (0, shared_1.appendQuery)(resolvedPath, new URLSearchParams(resolvedParams).toString()),
|
|
109
|
-
});
|
|
84
|
+
return processRedirect(exitPath, Object.assign(Object.assign({}, query), { shop, redirectUri: (0, shared_1.appendQuery)(resolvedPath, new URLSearchParams(resolvedParams).toString()) }));
|
|
110
85
|
}
|
|
111
86
|
const existingSessionIds = yield findSessionIds(shop);
|
|
112
87
|
// NOTE: this handles first time installs
|
|
@@ -116,17 +91,8 @@ const protectRoute = (serverRuntimeConfig, _props) => {
|
|
|
116
91
|
}
|
|
117
92
|
catch (error) {
|
|
118
93
|
console.error('Error storing session', error);
|
|
119
|
-
const foorl = `${PAGE_PATHS[shared_1.PAGE_ROUTE_NAMES.ERROR]}?type=protectRoute`;
|
|
120
|
-
console.info(`>>> ~ foorl`, foorl);
|
|
121
94
|
// TODO add custom 'code=' param for this use case
|
|
122
|
-
return
|
|
123
|
-
// return {
|
|
124
|
-
// redirect: {
|
|
125
|
-
// // TODO add custom 'code=' param for this use case
|
|
126
|
-
// destination: foorl,
|
|
127
|
-
// permanent: false,
|
|
128
|
-
// },
|
|
129
|
-
// }
|
|
95
|
+
return processRedirect(errorPath, Object.assign(Object.assign({}, query), { shop }));
|
|
130
96
|
}
|
|
131
97
|
let appId;
|
|
132
98
|
try {
|
|
@@ -147,11 +113,7 @@ const protectRoute = (serverRuntimeConfig, _props) => {
|
|
|
147
113
|
// NOTE: handleEnsureBilling handles calling encryptStoreSession for offline session when shouldCheckBilling
|
|
148
114
|
const callbackUrl = yield handleEnsureBilling(offlineSession);
|
|
149
115
|
if ((0, shared_1.stringNotEmpty)(callbackUrl)) {
|
|
150
|
-
return
|
|
151
|
-
shop,
|
|
152
|
-
query,
|
|
153
|
-
url: callbackUrl,
|
|
154
|
-
});
|
|
116
|
+
return processRedirect(exitPath, Object.assign(Object.assign({}, query), { shop, url: callbackUrl }));
|
|
155
117
|
}
|
|
156
118
|
}
|
|
157
119
|
}
|
|
@@ -160,49 +122,26 @@ const protectRoute = (serverRuntimeConfig, _props) => {
|
|
|
160
122
|
// #endregion EMBEDDED APP FLOW
|
|
161
123
|
// NOTE: this flow is not currently being use dby any apps
|
|
162
124
|
// #region NON_EMBEDDED APP FLOW
|
|
125
|
+
const authPath = API_PATHS[shared_1.API_ROUTE_NAMES.AUTH_ONLINE];
|
|
163
126
|
const existingSessionIds = yield findSessionIds(shop);
|
|
164
127
|
if ((0, shared_1.arrayEmpty)(existingSessionIds)) {
|
|
165
|
-
|
|
166
|
-
const queryParams = new URLSearchParams(Object.assign(Object.assign({}, query), { shop })).toString();
|
|
167
|
-
return {
|
|
168
|
-
redirect: {
|
|
169
|
-
destination: `${API_PATHS[shared_1.API_ROUTE_NAMES.AUTH_ONLINE]}?${queryParams}`,
|
|
170
|
-
permanent: false,
|
|
171
|
-
},
|
|
172
|
-
};
|
|
128
|
+
return processRedirect(authPath, Object.assign(Object.assign({}, query), { shop }));
|
|
173
129
|
}
|
|
174
130
|
try {
|
|
175
131
|
// NOTE: this does not work if called INSIDE of embedded
|
|
176
132
|
yield loadSession({ req, res });
|
|
177
|
-
return {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
rawResponse: res,
|
|
182
|
-
}),
|
|
183
|
-
permanent: false,
|
|
184
|
-
},
|
|
185
|
-
};
|
|
133
|
+
return processRedirect(yield shopify.auth.getEmbeddedAppUrl({
|
|
134
|
+
rawRequest: req,
|
|
135
|
+
rawResponse: res,
|
|
136
|
+
}));
|
|
186
137
|
}
|
|
187
138
|
catch (error) {
|
|
188
139
|
if (error instanceof shared_1.ShopifyAuthenticationError) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
redirect: {
|
|
193
|
-
destination: `${API_PATHS[shared_1.API_ROUTE_NAMES.AUTH_ONLINE]}?${queryParams}`,
|
|
194
|
-
permanent: false,
|
|
195
|
-
},
|
|
196
|
-
};
|
|
140
|
+
const reAuth = (error instanceof shared_1.ShopifySessionAuthenticationError);
|
|
141
|
+
return processRedirect(authPath, Object.assign(Object.assign({}, query), { shop, reAuth }));
|
|
197
142
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// redirect: {
|
|
201
|
-
// // TODO add custom 'code=' param for this use case
|
|
202
|
-
// destination: `${PAGE_PATHS[PAGE_ROUTE_NAMES.ERROR]}?type=protectRoute`,
|
|
203
|
-
// permanent: false,
|
|
204
|
-
// },
|
|
205
|
-
// }
|
|
143
|
+
// TODO add custom 'code=' param for this use case
|
|
144
|
+
return processRedirect(errorPath, Object.assign(Object.assign({}, query), { shop }));
|
|
206
145
|
}
|
|
207
146
|
// #endregion NON_EMBEDDED APP FLOW
|
|
208
147
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protect.js","sourceRoot":"","sources":["../../../../src/lib/handlers/ssr/protect.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAAyB;AAEzB,uCAKoB;AACpB,
|
|
1
|
+
{"version":3,"file":"protect.js","sourceRoot":"","sources":["../../../../src/lib/handlers/ssr/protect.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAAyB;AAEzB,uCAKoB;AACpB,4CAiBwB;AAExB,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAG,KAAK,EAAE,EAAE;IACzD,oCAAoC;IACpC,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC,IAAA,qBAAY,EAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;IACxE,OAAO,CAAC,IAAI,CAAC,uBAAuB,EAAE;QACpC,WAAW;QACX,MAAM;QACN,GAAG;KACJ,CAAC,CAAA;IAEF,OAAO;QACL,QAAQ,EAAE;YACR,WAAW,EAAE,GAAG,GAAG,GAAG,IAAA,uBAAc,EAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YAC5E,SAAS;SACV;KACF,CAAA;AACH,CAAC,CAAA;AAEM,MAAM,YAAY,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,EAAE;IAC1D,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,mBAAmB,CAAA;IACjF,MAAM,UAAU,GAAG,UAAU,CAAC,aAAa,CAAA;IAC3C,MAAM,QAAQ,GAAG,UAAU,CAAC,yBAAgB,CAAC,IAAI,CAAC,CAAA;IAClD,MAAM,SAAS,GAAG,UAAU,CAAC,yBAAgB,CAAC,KAAK,CAAC,CAAA;IAEpD,OAAO,CAAO,EACZ,GAAG,EACH,GAAG,EACH,KAAK,EACL,MAAM,EACN,WAAW,EAAE,kCAAkC;MAChD,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE;;QACd,MAAM,CAAC,IAAI,CAAC,GAAG,IAAA,oBAAW,EAAC,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,mCAAI,MAAA,GAAG,CAAC,KAAK,0CAAE,IAAI,CAAC,CAAA;QAE1D,IAAI,IAAA,oBAAW,EAAC,IAAI,CAAC,EAAE;YACrB,OAAO,eAAe,CAAC,SAAS,kCAAO,KAAK,KAAE,IAAI,EAAE,kBAAkB,IAAG,CAAA;SAC1E;QAED,MAAM,KAAK,GAAG,IAAA,oBAAW,gCAAG,MAAM,IAAK,MAAM,GAAK,IAAI,GAAI,KAAK,EAAE,wBAAe,CAAC,CAAA;QAEjF,6EAA6E;QAC7E,MAAM,mBAAmB,GAAG,IAAA,wBAAgB,EAAC,mBAAmB,CAAC,CAAA;QACjE,IAAI,mBAAmB,EAAE;YACvB,OAAO,CAAC,IAAI,CAAC,GAAG,eAAK,CAAC,YAAY,CAAC,cAAc,CAAC,OAAO,eAAK,CAAC,IAAI,CAAC,cAAc,CAAC,MACjF,sGACF,EAAE,CAAC,CAAA;YAEH,OAAO,EAAE,KAAK,EAAE,CAAA;SACjB;QAED,MAAM,EACJ,OAAO,EACP,WAAW,EACX,WAAW,EACX,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EAAE,mBAAmB,GACnC,GAAG,MAAM,IAAA,yBAAiB,EAAC,mBAAmB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;QAE1D,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,EAAE;YACnB,GAAG,CAAC,SAAS,CAAC,yBAAyB,EAAE,IAAA,yBAAiB,EAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;SAClF;QAED,4BAA4B;QAC5B,IAAI,UAAU,EAAE;YACd,MAAM,OAAO,GAAG,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,4BAAmB,CAAC,mCAAI,MAAA,GAAG,CAAC,KAAK,0CAAG,4BAAmB,CAAC,CAAA;YAChF,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,oBAAoB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;YAEzE,IAAI,OAAO,GAAG,YAAY,CAAA;YAC1B,IAAI,cAAc,CAAA;YAElB,IAAI,OAAO,EAAE;gBACX,MAAM,EACJ,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,EACX,OAAO,GACR,GAAG,EAAE,GACP,GAAG,MAAM,oBAAoB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;gBAE3C,OAAO,GAAG,eAAe,CAAA;gBACzB,cAAc,GAAG,OAAO,CAAA;aACzB;YAED,8CAA8C;YAC9C,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE;gBAC/B,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,IAAA,qBAAY,EAAC,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;gBAC5E,6DAA6D;gBAC7D,MAAgE,KAAA,IAAA,4BAAmB,EAAC,eAAe,CAAC,EAA5F,KAAC,4BAAoB,EAAE,SAAS,SAAA,EAAK,cAAc,cAArD,uCAAuD,CAAuC,CAAA;gBACpG,OAAO,eAAe,CAAC,QAAQ,kCAC1B,KAAK,KACR,IAAI,EACJ,WAAW,EAAE,IAAA,oBAAW,EAAC,YAAY,EAAE,IAAI,eAAe,CAAC,cAAc,CAAC,CAAC,QAAQ,EAAE,CAAC,IACtF,CAAA;aACH;YAED,MAAM,kBAAkB,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAA;YAErD,yCAAyC;YACzC,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,EAAE;gBACnD,IAAI;oBACF,MAAM,YAAY,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAA;iBAC9C;gBAAC,OAAO,KAAK,EAAE;oBACd,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAA;oBAE7C,kDAAkD;oBAClD,OAAO,eAAe,CAAC,SAAS,kCAAO,KAAK,KAAE,IAAI,IAAG,CAAA;iBACtD;gBAED,IAAI,KAAK,CAAA;gBACT,IAAI;oBACF,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,WAAW,EAAE,CAAA;oBACvC,KAAK,GAAG,IAAA,2BAAkB,EAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,EAAE,CAAC,CAAC,EAAE,CAAA;oBAC5C,oCAAoC;iBACnC;gBAAC,OAAO,OAAO,EAAE;oBAChB,OAAO,CAAC,IAAI,CAAC,gCAAgC,EAAE,OAAO,CAAC,CAAA;iBACxD;gBAED,oBAAoB;gBACpB,MAAM,sBAAsB,GAAG,IAAA,iCAAyB,EAAC,mBAAmB,CAAC,CAAA;gBAC7E,MAAM,eAAe,GAAG,sBAAsB,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;gBACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAA;gBAC7C,gIAAgI;gBAChI,MAAM,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAA;gBAE5D,IAAI,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,QAAQ,EAAE;oBAC5B,4GAA4G;oBAC5G,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,cAAc,CAAC,CAAA;oBAE7D,IAAI,IAAA,uBAAc,EAAC,WAAW,CAAC,EAAE;wBAC/B,OAAO,eAAe,CAAC,QAAQ,kCAAO,KAAK,KAAE,IAAI,EAAE,GAAG,EAAE,WAAW,IAAG,CAAA;qBACvE;iBACF;aACF;YAED,OAAO,EAAE,KAAK,EAAE,CAAA;SACjB;QACD,+BAA+B;QAE/B,0DAA0D;QAC1D,gCAAgC;QAChC,MAAM,QAAQ,GAAG,SAAS,CAAC,wBAAe,CAAC,WAAW,CAAC,CAAA;QACvD,MAAM,kBAAkB,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAA;QAErD,IAAI,IAAA,mBAAU,EAAC,kBAAkB,CAAC,EAAE;YAClC,OAAO,eAAe,CAAC,QAAQ,kCAAO,KAAK,KAAE,IAAI,IAAG,CAAA;SACrD;QAED,IAAI;YACF,wDAAwD;YACxD,MAAM,WAAW,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;YAE/B,OAAO,eAAe,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC;gBAC1D,UAAU,EAAE,GAAG;gBACf,WAAW,EAAE,GAAG;aACjB,CAAC,CAAC,CAAA;SACJ;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,KAAK,YAAY,mCAA0B,EAAE;gBAC/C,MAAM,MAAM,GAAG,CAAC,KAAK,YAAY,0CAAiC,CAAC,CAAA;gBACnE,OAAO,eAAe,CAAC,QAAQ,kCAAO,KAAK,KAAE,IAAI,EAAE,MAAM,IAAG,CAAA;aAC7D;YAED,kDAAkD;YAClD,OAAO,eAAe,CAAC,SAAS,kCAAO,KAAK,KAAE,IAAI,IAAG,CAAA;SACtD;QACD,mCAAmC;IACrC,CAAC,CAAA,CAAA;AACH,CAAC,CAAA;AArJY,QAAA,YAAY,gBAqJxB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agnostack/next-shopify",
|
|
3
|
-
"version": "2.1.1-alpha.
|
|
3
|
+
"version": "2.1.1-alpha.28",
|
|
4
4
|
"author": "agnoStack Dev <developers@agnostack.com> (https://agnostack.com)",
|
|
5
5
|
"owner": "agnoStack",
|
|
6
6
|
"description": "Please contact agnoStack via info@agnostack.com for any questions",
|