roboto-js 1.7.0 → 1.7.4

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,235 @@
1
+ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
2
+ function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
3
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
4
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
5
+ function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
6
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
8
+ function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
9
+ function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
10
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
11
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
12
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
13
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
14
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
15
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
16
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
17
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
18
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
19
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
20
+ import _ from 'lodash';
21
+ var RbtUser = /*#__PURE__*/function () {
22
+ function RbtUser(record, axiosInstance) {
23
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
24
+ _classCallCheck(this, RbtUser);
25
+ this._axios = axiosInstance;
26
+ this._internalData = record;
27
+ this.id = record.id;
28
+ this.id_revision = record.id_revision;
29
+ this.rpcMeta = {
30
+ changes: [],
31
+ isNew: options.isNew || false
32
+ };
33
+ if (record.data) {
34
+ this._data = this._deepUnpackJson(record.data);
35
+ } else {
36
+ this._data = record.dataJson ? this._deepUnpackJson(record.dataJson) : {};
37
+ }
38
+ }
39
+ return _createClass(RbtUser, [{
40
+ key: "get",
41
+ value: function get(path) {
42
+ return _.get(this._data, path);
43
+ }
44
+ }, {
45
+ key: "getData",
46
+ value: function getData() {
47
+ return _objectSpread({}, this._data);
48
+ }
49
+ }, {
50
+ key: "_addChange",
51
+ value: function _addChange(path) {
52
+ // Ensure no duplicate paths
53
+ if (!this.rpcMeta.changes.includes(path)) {
54
+ this.rpcMeta.changes.push(path);
55
+ }
56
+ }
57
+ }, {
58
+ key: "set",
59
+ value: function set(path, value) {
60
+ var currentValue = _.get(this._data, path);
61
+ if (!_.isEqual(currentValue, value)) {
62
+ _.set(this._data, path, value);
63
+ this._addChange(path);
64
+ }
65
+ }
66
+ }, {
67
+ key: "setData",
68
+ value: function setData(newData) {
69
+ var _this = this;
70
+ if (_typeof(newData) !== 'object' || newData === null) {
71
+ throw new Error('setData expects an object');
72
+ }
73
+ Object.keys(newData).forEach(function (key) {
74
+ if (!_.isEqual(_.get(_this._data, key), newData[key])) {
75
+ _.set(_this._data, key, newData[key]);
76
+ _this._addChange(key);
77
+ }
78
+ });
79
+ }
80
+
81
+ //
82
+ // For Arrays
83
+ //
84
+ }, {
85
+ key: "setAppend",
86
+ value: function setAppend(key, value) {
87
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
88
+ var existingValue = this.get(key) || []; // Get the existing value or default to an empty array
89
+ if (!Array.isArray(existingValue)) {
90
+ existingValue = []; // Ensure existingValue is an array if not already
91
+ }
92
+ var valuesToAdd = Array.isArray(value) ? value : [value]; // Convert value to an array if it's not one
93
+
94
+ // Combine existingValue and valuesToAdd, filtering out duplicates
95
+ var mergedValues = Array.from(new Set([].concat(_toConsumableArray(existingValue), _toConsumableArray(valuesToAdd))));
96
+ this.set(key, mergedValues, options); // Set the updated array back to the user data
97
+ }
98
+ }, {
99
+ key: "setRemove",
100
+ value: function setRemove(key, value) {
101
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
102
+ var existingValue = this.get(key) || []; // Get the existing value or default to an empty array
103
+ if (!Array.isArray(existingValue)) {
104
+ return; // If it's not an array, there's nothing to remove, so exit early
105
+ }
106
+ var valuesToRemove = Array.isArray(value) ? value : [value]; // Convert value to an array if it's not one
107
+
108
+ // Filter out the values to remove from the existing value array
109
+ var filteredValues = existingValue.filter(function (item) {
110
+ return !valuesToRemove.includes(item);
111
+ });
112
+ this.set(key, filteredValues); // Set the updated array back to the user data
113
+ }
114
+
115
+ //
116
+ //
117
+ //
118
+ }, {
119
+ key: "toRecord",
120
+ value: function toRecord() {
121
+ return _objectSpread(_objectSpread({}, this._internalData), {}, {
122
+ dataJson: JSON.stringify(this._data),
123
+ rpcMeta: this.rpcMeta
124
+ });
125
+ }
126
+ }, {
127
+ key: "clone",
128
+ value: function clone() {
129
+ // Create a deep copy of the current object's data
130
+ var clonedData = _.cloneDeep(this._internalData);
131
+
132
+ // Reset unique identifiers to ensure a new ID is generated upon saving
133
+ delete clonedData.id;
134
+ delete clonedData.id_revision;
135
+
136
+ // Create a new instance of RbtUser with the cloned data
137
+ var clonedObject = new RbtUser(clonedData, this._axios, {
138
+ isNew: true
139
+ });
140
+ return clonedObject;
141
+ }
142
+ }, {
143
+ key: "save",
144
+ value: function () {
145
+ var _save = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
146
+ var record, response, _t;
147
+ return _regenerator().w(function (_context) {
148
+ while (1) switch (_context.p = _context.n) {
149
+ case 0:
150
+ _context.p = 0;
151
+ debugger;
152
+ record = this.toRecord();
153
+ record.type = '<@iac.user>';
154
+ _context.n = 1;
155
+ return this._axios.post('/user_service/saveUser', [record]);
156
+ case 1:
157
+ response = _context.v;
158
+ if (!(response.data.ok === false)) {
159
+ _context.n = 2;
160
+ break;
161
+ }
162
+ throw new Error(response.data.message);
163
+ case 2:
164
+ this._internalData = response.data;
165
+ this.id = response.data.id;
166
+ this.id_revision = response.data.id_revision;
167
+ this.type = response.data.type;
168
+ this.rpcMeta.isNew = false;
169
+ return _context.a(2, this);
170
+ case 3:
171
+ _context.p = 3;
172
+ _t = _context.v;
173
+ console.log('RbtUser.save.error:', _t);
174
+ //console.log(e.response.data);
175
+ throw _t;
176
+ case 4:
177
+ return _context.a(2);
178
+ }
179
+ }, _callee, this, [[0, 3]]);
180
+ }));
181
+ function save() {
182
+ return _save.apply(this, arguments);
183
+ }
184
+ return save;
185
+ }()
186
+ }, {
187
+ key: "_deepUnpackJson",
188
+ value: function _deepUnpackJson(value) {
189
+ if (typeof value === 'string') {
190
+ try {
191
+ // Only parse as JSON if it's not a large number
192
+ // https://chatgpt.com/c/6745902c-edf4-800c-ab52-31bf27dde2bd
193
+ //
194
+ if (!/^\d{16,}$/.test(value)) {
195
+ var parsed = JSON.parse(value);
196
+ // Recursively parse if the result is a string, object, or array
197
+ return this._deepUnpackJson(parsed);
198
+ }
199
+ } catch (e) {
200
+ return value; // Return the original string if parsing fails
201
+ }
202
+ } else if (value !== null && _typeof(value) === 'object') {
203
+ // If it's an object (including arrays), recursively parse each value
204
+ for (var key in value) {
205
+ value[key] = this._deepUnpackJson(value[key]);
206
+ }
207
+ }
208
+ return value;
209
+ }
210
+
211
+ //async delete() {
212
+ // if (!this._internalData.type) {
213
+ // throw new Error('Cannot delete object without type');
214
+ // }
215
+ //
216
+ // try {
217
+ // const record = this.toRecord();
218
+ // const response = await this._axios.post('/object_service/deleteObject', [record]);
219
+ //
220
+ // if (response.data.ok === false) {
221
+ // throw new Error(response.data.message);
222
+ // }
223
+ //
224
+ // this._internalData = response.data;
225
+ // return this;
226
+ //
227
+ // } catch (e) {
228
+ // console.log('RbtUser.delete.error:');
229
+ // console.log(e.response.data);
230
+ // throw e;
231
+ // }
232
+ //}
233
+ }]);
234
+ }();
235
+ export { RbtUser as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roboto-js",
3
- "version": "1.7.0",
3
+ "version": "1.7.4",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "main": "dist/cjs/index.cjs",
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Cookie-based storage adaptor for roboto-js
3
+ * Provides a localStorage-compatible interface using cookies
4
+ * This enables server-side access to authentication tokens
5
+ */
6
+
7
+ export default class CookieStorageAdaptor {
8
+ constructor(options = {}) {
9
+ this.options = {
10
+ // Security settings
11
+ secure: options.secure ?? (typeof window !== 'undefined' && window.location.protocol === 'https:'),
12
+ sameSite: options.sameSite ?? 'Lax',
13
+ httpOnly: false, // Must be false so client-side JS can access
14
+
15
+ // Cookie settings
16
+ path: options.path ?? '/',
17
+ maxAge: options.maxAge ?? 24 * 60 * 60, // 24 hours default
18
+ domain: options.domain ?? undefined, // Let browser determine domain
19
+
20
+ // Prefix for roboto cookies to avoid conflicts
21
+ prefix: options.prefix ?? 'rbt_',
22
+
23
+ // Keys that should be stored without prefix for server-side access
24
+ serverAccessKeys: options.serverAccessKeys ?? ['authtoken', 'accessKey', 'apikey'],
25
+
26
+ ...options
27
+ }
28
+
29
+ console.log('[CookieStorageAdaptor] Initialized with options:', {
30
+ secure: this.options.secure,
31
+ sameSite: this.options.sameSite,
32
+ path: this.options.path,
33
+ maxAge: this.options.maxAge,
34
+ domain: this.options.domain,
35
+ prefix: this.options.prefix,
36
+ serverAccessKeys: this.options.serverAccessKeys
37
+ })
38
+ }
39
+
40
+ /**
41
+ * Get item from cookies (localStorage-compatible interface)
42
+ */
43
+ async getItem(key) {
44
+ if (typeof document === 'undefined') {
45
+ console.log(`[CookieStorageAdaptor] getItem(${key}): document undefined, returning null`)
46
+ return null
47
+ }
48
+
49
+ // Check if this key should be stored without prefix for server access
50
+ const usePrefix = !this.options.serverAccessKeys.includes(key)
51
+ const cookieName = usePrefix ? this.options.prefix + key : key
52
+ const name = cookieName + '='
53
+ const decodedCookie = decodeURIComponent(document.cookie)
54
+ const cookies = decodedCookie.split(';')
55
+
56
+ console.log(`[CookieStorageAdaptor] getItem(${key}): looking for cookie "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
57
+ console.log(`[CookieStorageAdaptor] Available cookies:`, cookies.map(c => c.trim().split('=')[0]).join(', '))
58
+
59
+ for (let cookie of cookies) {
60
+ cookie = cookie.trim()
61
+ if (cookie.indexOf(name) === 0) {
62
+ const rawValue = cookie.substring(name.length, cookie.length)
63
+ console.log(`[CookieStorageAdaptor] Found cookie "${cookieName}" with raw value:`, rawValue)
64
+
65
+ // Handle JSON values (like rbtUser)
66
+ try {
67
+ const parsedValue = JSON.parse(rawValue)
68
+ console.log(`[CookieStorageAdaptor] getItem(${key}): returning parsed JSON:`, parsedValue)
69
+ return parsedValue
70
+ } catch {
71
+ console.log(`[CookieStorageAdaptor] getItem(${key}): returning string value:`, rawValue)
72
+ return rawValue
73
+ }
74
+ }
75
+ }
76
+
77
+ console.log(`[CookieStorageAdaptor] getItem(${key}): cookie "${cookieName}" not found, returning null`)
78
+ return null
79
+ }
80
+
81
+ /**
82
+ * Set item in cookies (localStorage-compatible interface)
83
+ */
84
+ async setItem(key, value) {
85
+ if (typeof document === 'undefined') {
86
+ console.log(`[CookieStorageAdaptor] setItem(${key}): document undefined, skipping`)
87
+ return
88
+ }
89
+
90
+ // Check if this key should be stored without prefix for server access
91
+ const usePrefix = !this.options.serverAccessKeys.includes(key)
92
+ const cookieName = usePrefix ? this.options.prefix + key : key
93
+
94
+ // Stringify objects/arrays like localStorage does
95
+ const cookieValue = typeof value === 'object' ? JSON.stringify(value) : String(value)
96
+
97
+ console.log(`[CookieStorageAdaptor] setItem(${key}): storing as "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
98
+ console.log(`[CookieStorageAdaptor] Original value:`, value)
99
+ console.log(`[CookieStorageAdaptor] Cookie value:`, cookieValue)
100
+
101
+ // Build cookie string with security options
102
+ const secureFlag = this.options.secure ? '; Secure' : ''
103
+ const domainFlag = this.options.domain ? `; Domain=${this.options.domain}` : ''
104
+ const httpOnlyFlag = this.options.httpOnly ? '; HttpOnly' : ''
105
+
106
+ const cookieString = `${cookieName}=${encodeURIComponent(cookieValue)}; path=${this.options.path}; max-age=${this.options.maxAge}; SameSite=${this.options.sameSite}${secureFlag}${domainFlag}${httpOnlyFlag}`
107
+
108
+ console.log(`[CookieStorageAdaptor] Full cookie string:`, cookieString)
109
+
110
+ document.cookie = cookieString
111
+
112
+ // Verify the cookie was set by immediately reading it back
113
+ const verification = await this.getItem(key)
114
+ if (verification !== null) {
115
+ console.log(`[CookieStorageAdaptor] ✅ Successfully set and verified cookie: ${cookieName}`)
116
+ } else {
117
+ console.error(`[CookieStorageAdaptor] ❌ Failed to set cookie: ${cookieName}`)
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Remove item from cookies (localStorage-compatible interface)
123
+ */
124
+ async removeItem(key) {
125
+ if (typeof document === 'undefined') {
126
+ console.log(`[CookieStorageAdaptor] removeItem(${key}): document undefined, skipping`)
127
+ return
128
+ }
129
+
130
+ // Check if this key should be stored without prefix for server access
131
+ const usePrefix = !this.options.serverAccessKeys.includes(key)
132
+ const cookieName = usePrefix ? this.options.prefix + key : key
133
+
134
+ console.log(`[CookieStorageAdaptor] removeItem(${key}): removing cookie "${cookieName}" ${usePrefix ? '(prefixed)' : '(server-accessible)'}`)
135
+
136
+ // Check if cookie exists before removal
137
+ const existingValue = await this.getItem(key)
138
+ if (existingValue !== null) {
139
+ console.log(`[CookieStorageAdaptor] Cookie "${cookieName}" exists, removing...`)
140
+ } else {
141
+ console.log(`[CookieStorageAdaptor] Cookie "${cookieName}" doesn't exist, removal not needed`)
142
+ }
143
+
144
+ const secureFlag = this.options.secure ? '; Secure' : ''
145
+ const domainFlag = this.options.domain ? `; Domain=${this.options.domain}` : ''
146
+
147
+ const removalString = `${cookieName}=; path=${this.options.path}; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=${this.options.sameSite}${secureFlag}${domainFlag}`
148
+ console.log(`[CookieStorageAdaptor] Removal cookie string:`, removalString)
149
+
150
+ document.cookie = removalString
151
+
152
+ // Verify the cookie was removed
153
+ const verification = await this.getItem(key)
154
+ if (verification === null) {
155
+ console.log(`[CookieStorageAdaptor] ✅ Successfully removed cookie: ${cookieName}`)
156
+ } else {
157
+ console.error(`[CookieStorageAdaptor] ❌ Failed to remove cookie: ${cookieName}, still has value:`, verification)
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Clear all roboto cookies
163
+ */
164
+ async clear() {
165
+ if (typeof document === 'undefined') return
166
+
167
+ // Get all cookies and remove ones with our prefix or server access keys
168
+ const cookies = document.cookie.split(';')
169
+ const prefix = this.options.prefix
170
+
171
+ for (let cookie of cookies) {
172
+ const cookieName = cookie.split('=')[0].trim()
173
+
174
+ // Remove prefixed cookies
175
+ if (cookieName.startsWith(prefix)) {
176
+ const key = cookieName.substring(prefix.length)
177
+ await this.removeItem(key)
178
+ }
179
+ // Remove server access keys (non-prefixed)
180
+ else if (this.options.serverAccessKeys.includes(cookieName)) {
181
+ await this.removeItem(cookieName)
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Get all keys (for debugging/compatibility)
188
+ */
189
+ async keys() {
190
+ if (typeof document === 'undefined') return []
191
+
192
+ const cookies = document.cookie.split(';')
193
+ const prefix = this.options.prefix
194
+ const keys = []
195
+
196
+ for (let cookie of cookies) {
197
+ const cookieName = cookie.split('=')[0].trim()
198
+
199
+ // Add prefixed cookies
200
+ if (cookieName.startsWith(prefix)) {
201
+ keys.push(cookieName.substring(prefix.length))
202
+ }
203
+ // Add server access keys (non-prefixed)
204
+ else if (this.options.serverAccessKeys.includes(cookieName)) {
205
+ keys.push(cookieName)
206
+ }
207
+ }
208
+
209
+ return keys
210
+ }
211
+
212
+ /**
213
+ * Debug method to log all current cookies and adapter state
214
+ */
215
+ debugState() {
216
+ if (typeof document === 'undefined') {
217
+ console.log('[CookieStorageAdaptor] DEBUG: document undefined (server-side)')
218
+ return
219
+ }
220
+
221
+ console.log('[CookieStorageAdaptor] DEBUG STATE:')
222
+ console.log('- Options:', this.options)
223
+ console.log('- All cookies:', document.cookie)
224
+
225
+ const cookies = document.cookie.split(';')
226
+ const robotoKeys = []
227
+ const serverKeys = []
228
+ const otherKeys = []
229
+
230
+ for (let cookie of cookies) {
231
+ const cookieName = cookie.split('=')[0].trim()
232
+ if (cookieName.startsWith(this.options.prefix)) {
233
+ robotoKeys.push(cookieName)
234
+ } else if (this.options.serverAccessKeys.includes(cookieName)) {
235
+ serverKeys.push(cookieName)
236
+ } else if (cookieName) {
237
+ otherKeys.push(cookieName)
238
+ }
239
+ }
240
+
241
+ console.log('- Roboto prefixed cookies:', robotoKeys)
242
+ console.log('- Server access cookies:', serverKeys)
243
+ console.log('- Other cookies:', otherKeys)
244
+ }
245
+
246
+ }
package/src/index.js CHANGED
@@ -3,11 +3,13 @@ import RbtApi from './rbt_api.js';
3
3
  import RbtObject from './rbt_object.js';
4
4
  import RbtFile from './rbt_file.js';
5
5
  import RbtMetricsApi from './rbt_metrics_api.js';
6
+ import CookieStorageAdaptor from './cookie_storage_adaptor.js';
6
7
 
7
8
  export {
8
9
  RbtApi,
9
10
  RbtObject,
10
- RbtFile
11
+ RbtFile,
12
+ CookieStorageAdaptor
11
13
  //User,
12
14
  //Site
13
15
  };
@@ -15,10 +17,10 @@ export {
15
17
  export default class Roboto{
16
18
 
17
19
  getVersion(){
18
- return '1.6.17';
20
+ return '1.7.3';
19
21
  }
20
22
 
21
- constructor({ host, accessKey, localStorageAdaptor, disableWebSocket = false, metricsHost }, proxyReq = null) {
23
+ constructor({ host, accessKey, localStorageAdaptor, disableWebSocket = false, metricsHost, useCookies = true }, proxyReq = null) {
22
24
 
23
25
  if (Roboto.instance && !proxyReq) {
24
26
  // if on client, there can only be one instance
@@ -28,10 +30,30 @@ export default class Roboto{
28
30
 
29
31
  const isBrowser = typeof window !== "undefined";
30
32
  this.data = {};
33
+
34
+ // Auto-configure storage adaptor
35
+ let storageAdaptor = localStorageAdaptor;
36
+ if (!storageAdaptor && isBrowser && useCookies) {
37
+ // Use cookies by default in browser for better server-side compatibility
38
+ storageAdaptor = new CookieStorageAdaptor({
39
+ secure: window.location.protocol === 'https:',
40
+ sameSite: 'Lax',
41
+ maxAge: 24 * 60 * 60 // 24 hours
42
+ });
43
+ console.log('[Roboto] Using CookieStorageAdaptor for authentication tokens');
44
+
45
+ // Set accessKey for server-side authentication (handled automatically by adapter)
46
+ if (accessKey) {
47
+ storageAdaptor.setItem('accessKey', accessKey).catch(e =>
48
+ console.warn('[Roboto] Failed to set accessKey cookie:', e)
49
+ );
50
+ }
51
+ }
52
+
31
53
  this.config = {
32
54
  accessKey: accessKey, // Use passed accessKey
33
55
  baseUrl: `https://${host}`, // Use passed host
34
- localStorageAdaptor: localStorageAdaptor
56
+ localStorageAdaptor: storageAdaptor
35
57
  };
36
58
 
37
59
  // DEVELOPMENT
@@ -58,6 +80,12 @@ export default class Roboto{
58
80
  this.api = new RbtApi(this.config);
59
81
  if (isBrowser) {
60
82
  this.api.initLocalDb();
83
+
84
+ // Add global debug method for cookie storage adapter
85
+ if (this.config.localStorageAdaptor && this.config.localStorageAdaptor.debugState) {
86
+ window.debugRobotoCookies = () => this.config.localStorageAdaptor.debugState();
87
+ console.log('[Roboto] Added global debug method: window.debugRobotoCookies()');
88
+ }
61
89
  }
62
90
 
63
91
  // METRICS API instance (separate host or same)
@@ -155,7 +183,15 @@ export default class Roboto{
155
183
  return this.api.loginWithOauth(params);
156
184
  }
157
185
  async logout(){
158
- return this.api.logout();
186
+ const result = await this.api.logout();
187
+
188
+ // Clear accessKey and authtoken using standard localStorage interface
189
+ if (this.config.localStorageAdaptor) {
190
+ await this.config.localStorageAdaptor.removeItem('accessKey');
191
+ await this.config.localStorageAdaptor.removeItem('authtoken');
192
+ }
193
+
194
+ return result;
159
195
  }
160
196
  async refreshAuthToken(){
161
197
  return this.api.refreshAuthToken(this.config.authtoken);
package/src/rbt_api.js CHANGED
@@ -43,26 +43,9 @@ export default class RbtApi {
43
43
  };
44
44
  }
45
45
 
46
- // Synchronous browser hydration: set auth header and in-memory user immediately
47
- if (typeof localStorage !== 'undefined') {
48
- try {
49
- const token = localStorage.getItem('authtoken');
50
- if (token) {
51
- this.authtoken = token;
52
- this.axios.defaults.headers.common['authtoken'] = token;
53
- }
54
- } catch {}
55
- try {
56
- const cachedUser = localStorage.getItem('rbtUser');
57
- if (cachedUser) {
58
- const parsed = JSON.parse(cachedUser);
59
- if (parsed && parsed.id) {
60
- this.currentUser = new RbtUser({ id: parsed.id }, this.axios);
61
- this.currentUser.setData(parsed);
62
- }
63
- }
64
- } catch {}
65
- }
46
+ // Asynchronous browser hydration: set auth header and in-memory user
47
+ // Use storage adaptor if available, otherwise fallback to localStorage
48
+ this._initializeFromStorage().catch(e => console.warn('[RbtApi] Storage initialization failed:', e));
66
49
  this.localDb = null;
67
50
  this.iac_session = null;
68
51
  this.appServiceHost = baseUrl;
@@ -77,6 +60,52 @@ export default class RbtApi {
77
60
 
78
61
  }
79
62
 
63
+ // Initialize authtoken and user from storage (cookies or localStorage)
64
+ async _initializeFromStorage() {
65
+ if (!this.localStorageAdaptor) return;
66
+
67
+ try {
68
+ // Try to get authtoken from storage adaptor (prefixed: rbt_authtoken)
69
+ let token = await this.localStorageAdaptor.getItem('authtoken');
70
+
71
+ // If not found in prefixed storage, try raw cookie (like accessKey)
72
+ if (!token && typeof document !== 'undefined') {
73
+ // Try to get from raw cookie
74
+ const cookies = document.cookie.split(';');
75
+ for (let cookie of cookies) {
76
+ const [name, value] = cookie.trim().split('=');
77
+ if (name === 'authtoken') {
78
+ token = decodeURIComponent(value);
79
+ break;
80
+ }
81
+ }
82
+ }
83
+
84
+ if (token) {
85
+ this.authtoken = token;
86
+ this.axios.defaults.headers.common['authtoken'] = token;
87
+ console.log('[RbtApi] Loaded authtoken from storage adaptor');
88
+ }
89
+ } catch (e) {
90
+ console.warn('[RbtApi] Failed to load authtoken from storage adaptor:', e);
91
+ }
92
+
93
+ try {
94
+ // Try to get user from storage adaptor
95
+ const cachedUser = await this.localStorageAdaptor.getItem('rbtUser');
96
+ if (cachedUser) {
97
+ const parsed = typeof cachedUser === 'string' ? JSON.parse(cachedUser) : cachedUser;
98
+ if (parsed && parsed.id) {
99
+ this.currentUser = new RbtUser({ id: parsed.id }, this.axios);
100
+ this.currentUser.setData(parsed);
101
+ console.log('[RbtApi] Loaded user from storage adaptor');
102
+ }
103
+ }
104
+ } catch (e) {
105
+ console.warn('[RbtApi] Failed to load user from storage adaptor:', e);
106
+ }
107
+ }
108
+
80
109
  getWebSocketClient() {
81
110
  // Reuse existing WebSocket if it's OPEN or CONNECTING (to prevent race condition)
82
111
  if (this.websocketClient &&
@@ -277,6 +306,8 @@ export default class RbtApi {
277
306
  if (this.iac_session?.user) {
278
307
  await this.localStorageAdaptor.setItem('rbtUser', JSON.stringify(this.iac_session.user));
279
308
  }
309
+
310
+ // authtoken is automatically stored for server-side access by the adapter
280
311
  }
281
312
 
282
313
  return response.data;