@spacelr/sdk 0.4.0 → 0.5.1

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/dist/index.mjs CHANGED
@@ -825,7 +825,7 @@ var RealtimeClient = class {
825
825
  if (!where || Object.keys(where).length === 0) {
826
826
  return base;
827
827
  }
828
- const filterStr = Object.keys(where).sort().map((k) => `${k}=${String(where[k])}`).join("&");
828
+ const filterStr = Object.keys(where).sort().map((k) => `${k}=${JSON.stringify(where[k])}`).join("&");
829
829
  return `${base}?${filterStr}`;
830
830
  }
831
831
  async ensureConnected() {
@@ -973,15 +973,15 @@ var RealtimeClient = class {
973
973
  if (room === base) return true;
974
974
  if (!room.startsWith(`${base}?`)) return false;
975
975
  const where = this.roomWhereMap.get(room);
976
- if (!where) return true;
976
+ if (!where) return false;
977
977
  if (!event.document) return false;
978
978
  for (const [key, value] of Object.entries(where)) {
979
979
  const docValue = event.document[key];
980
980
  if (Array.isArray(docValue)) {
981
- if (!docValue.some((item) => String(item) === String(value))) {
981
+ if (!docValue.includes(value)) {
982
982
  return false;
983
983
  }
984
- } else if (String(docValue) !== String(value)) {
984
+ } else if (docValue !== value) {
985
985
  return false;
986
986
  }
987
987
  }
@@ -1784,6 +1784,144 @@ var StorageModule = class {
1784
1784
  }
1785
1785
  };
1786
1786
 
1787
+ // libs/sdk/src/modules/timeline.errors.ts
1788
+ var TimelineError = class extends SpacelrError {
1789
+ constructor(message, opts = {}) {
1790
+ super(message, opts.code ?? "TIMELINE_ERROR", opts.statusCode);
1791
+ this.name = "TimelineError";
1792
+ }
1793
+ };
1794
+ var CursorInvalidError = class extends TimelineError {
1795
+ constructor(message, opts = {}) {
1796
+ super(message, { statusCode: opts.statusCode ?? 400, code: opts.code ?? "CURSOR_INVALID" });
1797
+ this.name = "CursorInvalidError";
1798
+ }
1799
+ };
1800
+ var ValidationError = class extends TimelineError {
1801
+ constructor(message, opts = {}) {
1802
+ super(message, { statusCode: opts.statusCode ?? 400, code: opts.code ?? "BAD_REQUEST" });
1803
+ this.name = "ValidationError";
1804
+ }
1805
+ };
1806
+ var ForbiddenError = class extends TimelineError {
1807
+ constructor(message, opts = {}) {
1808
+ super(message, { statusCode: opts.statusCode ?? 403, code: opts.code ?? "FORBIDDEN" });
1809
+ this.name = "ForbiddenError";
1810
+ }
1811
+ };
1812
+ var NotFoundError = class extends TimelineError {
1813
+ constructor(message, opts = {}) {
1814
+ super(message, { statusCode: opts.statusCode ?? 404, code: opts.code ?? "NOT_FOUND" });
1815
+ this.name = "NotFoundError";
1816
+ }
1817
+ };
1818
+ var ServerConfigError = class extends TimelineError {
1819
+ constructor(message, opts = {}) {
1820
+ super(message, { statusCode: opts.statusCode ?? 500, code: opts.code ?? "CONFIG_INVALID" });
1821
+ this.name = "ServerConfigError";
1822
+ }
1823
+ };
1824
+ var TimeoutError = class extends TimelineError {
1825
+ constructor(message, opts = {}) {
1826
+ super(message, { statusCode: opts.statusCode ?? 504, code: opts.code ?? "TIMEOUT" });
1827
+ this.name = "TimeoutError";
1828
+ }
1829
+ };
1830
+ function mapTimelineError(statusCode, body) {
1831
+ const code = body?.code;
1832
+ const message = body?.message ?? `Timeline request failed with HTTP ${statusCode}`;
1833
+ if (statusCode === 400) {
1834
+ if (code === "CURSOR_INVALID") {
1835
+ return new CursorInvalidError(message, { statusCode, code });
1836
+ }
1837
+ return new ValidationError(message, { statusCode, code });
1838
+ }
1839
+ if (statusCode === 403) {
1840
+ return new ForbiddenError(message, { statusCode, code });
1841
+ }
1842
+ if (statusCode === 404) {
1843
+ return new NotFoundError(message, { statusCode, code });
1844
+ }
1845
+ if (statusCode === 500) {
1846
+ return new ServerConfigError(message, { statusCode, code });
1847
+ }
1848
+ if (statusCode === 504) {
1849
+ return new TimeoutError(message, { statusCode, code });
1850
+ }
1851
+ return new TimelineError(message, { statusCode, code });
1852
+ }
1853
+
1854
+ // libs/sdk/src/modules/timeline.module.ts
1855
+ var TimelineModule = class {
1856
+ constructor(http) {
1857
+ this.http = http;
1858
+ }
1859
+ async query(opts) {
1860
+ const body = {
1861
+ collection: opts.collection,
1862
+ partitionValue: opts.partitionValue
1863
+ };
1864
+ if (opts.where !== void 0) body["where"] = opts.where;
1865
+ if (opts.orderBy !== void 0) body["orderBy"] = opts.orderBy;
1866
+ if (opts.limit !== void 0) body["limit"] = opts.limit;
1867
+ if (opts.cursor !== void 0) body["cursor"] = opts.cursor;
1868
+ let raw;
1869
+ try {
1870
+ raw = await this.http.request({
1871
+ method: "POST",
1872
+ path: "/db/timeline",
1873
+ body,
1874
+ authenticated: true
1875
+ });
1876
+ } catch (err) {
1877
+ throw this.translateError(err);
1878
+ }
1879
+ if (!isTimelineQueryResponse(raw)) {
1880
+ throw new TimelineError(
1881
+ "Timeline gateway returned a response shape the SDK does not recognise",
1882
+ { statusCode: 500, code: "INVALID_RESPONSE_SHAPE" }
1883
+ );
1884
+ }
1885
+ return raw;
1886
+ }
1887
+ translateError(err) {
1888
+ if (err instanceof SpacelrTimeoutError) {
1889
+ return new TimeoutError(err.message, { statusCode: 504, code: "TIMEOUT" });
1890
+ }
1891
+ if (err instanceof SpacelrAuthError) {
1892
+ if (err.statusCode === 403) {
1893
+ return new ForbiddenError(err.message, { statusCode: 403, code: err.code ?? "FORBIDDEN" });
1894
+ }
1895
+ return err;
1896
+ }
1897
+ if (err instanceof SpacelrNetworkError) {
1898
+ return err;
1899
+ }
1900
+ if (err instanceof SpacelrError) {
1901
+ return mapTimelineError(err.statusCode ?? 0, { code: err.code, message: err.message });
1902
+ }
1903
+ return err;
1904
+ }
1905
+ };
1906
+ function isTimelineQueryResponse(value) {
1907
+ if (typeof value !== "object" || value === null) return false;
1908
+ const v = value;
1909
+ if (!Array.isArray(v["items"])) return false;
1910
+ if (v["nextCursor"] !== null && typeof v["nextCursor"] !== "string") return false;
1911
+ if (!isTimelineSourceStats(v["sourceStats"])) return false;
1912
+ return true;
1913
+ }
1914
+ function isTimelineSourceStats(value) {
1915
+ if (typeof value !== "object" || value === null) return false;
1916
+ const v = value;
1917
+ if (typeof v["hot"] !== "number") return false;
1918
+ if (typeof v["cold"] !== "number") return false;
1919
+ if (v["segmentsScanned"] !== void 0 && typeof v["segmentsScanned"] !== "number") {
1920
+ return false;
1921
+ }
1922
+ return true;
1923
+ }
1924
+
1787
1925
  // libs/sdk/src/modules/database.module.ts
1788
1926
  var Paginator = class {
1789
1927
  constructor(http, basePath, opts) {
@@ -1967,6 +2105,16 @@ var CollectionRef = class {
1967
2105
  authenticated: true
1968
2106
  });
1969
2107
  }
2108
+ /**
2109
+ * Query the collection's **hot tier** (live MongoDB).
2110
+ *
2111
+ * **Cold-tier note:** if the collection has cold-tier archival enabled, aged
2112
+ * documents are moved to object storage and purged from the hot tier — they
2113
+ * will NOT appear in `find()` results (nor `findById`, `count`, `search`,
2114
+ * `paginate`, which are all hot-tier only). To read archived history, use
2115
+ * {@link TimelineModule.query} via `db.timeline.query(...)`, which transparently
2116
+ * merges hot and cold results for a partition.
2117
+ */
1970
2118
  find(filter) {
1971
2119
  return new QueryBuilder(this.http, this.basePath, filter);
1972
2120
  }
@@ -2001,6 +2149,9 @@ var CollectionRef = class {
2001
2149
  *
2002
2150
  * Limits: `query` 1–200 chars, `fields` 1–10 entries (each matching
2003
2151
  * `/^[a-zA-Z0-9_.]+$/`, max 64 chars), `limit` max 100.
2152
+ *
2153
+ * **Cold-tier note:** searches the hot tier only; archived documents are not
2154
+ * included. See {@link CollectionRef.find} and `db.timeline.query(...)`.
2004
2155
  */
2005
2156
  async search(opts) {
2006
2157
  return this.http.request({
@@ -2010,6 +2161,13 @@ var CollectionRef = class {
2010
2161
  authenticated: true
2011
2162
  });
2012
2163
  }
2164
+ /**
2165
+ * Fetch a single document by `_id` from the **hot tier**.
2166
+ *
2167
+ * **Cold-tier note:** returns null/throws for documents that have been
2168
+ * archived and purged from the hot tier. Archived history is reachable only
2169
+ * via `db.timeline.query(...)`. See {@link CollectionRef.find}.
2170
+ */
2013
2171
  async findById(id, options) {
2014
2172
  const query = {};
2015
2173
  if (options?.populate?.length) {
@@ -2039,6 +2197,13 @@ var CollectionRef = class {
2039
2197
  authenticated: true
2040
2198
  });
2041
2199
  }
2200
+ /**
2201
+ * Count documents in the **hot tier** matching `filter`.
2202
+ *
2203
+ * **Cold-tier note:** counts hot-tier documents only — archived/purged
2204
+ * documents are not included, so this is not a total-history count. See
2205
+ * {@link CollectionRef.find} and `db.timeline.query(...)`.
2206
+ */
2042
2207
  async count(filter) {
2043
2208
  const result = await this.http.request({
2044
2209
  method: "POST",
@@ -2474,6 +2639,7 @@ var DatabaseModule = class {
2474
2639
  this.http = http;
2475
2640
  this.projectId = projectId;
2476
2641
  this.realtime = realtime ?? null;
2642
+ this.timeline = new TimelineModule(http);
2477
2643
  }
2478
2644
  collection(name) {
2479
2645
  return new CollectionRef(this.http, this.realtime, this.projectId, name);
@@ -2672,6 +2838,65 @@ var FunctionsModule = class {
2672
2838
  }
2673
2839
  };
2674
2840
 
2841
+ // libs/sdk/src/modules/schedule.module.ts
2842
+ var ScheduleModule = class {
2843
+ constructor(http, projectId) {
2844
+ this.http = http;
2845
+ this.projectId = projectId;
2846
+ }
2847
+ /**
2848
+ * Schedule a one-shot function invocation. Returns the existing handle
2849
+ * unchanged if `idempotencyKey` matches a prior invoke in this project
2850
+ * for this function.
2851
+ */
2852
+ async invoke(options) {
2853
+ return this.http.request({
2854
+ method: "POST",
2855
+ path: `/schedules/${encodeURIComponent(this.projectId)}`,
2856
+ body: {
2857
+ functionId: options.functionId,
2858
+ executeAt: this.toIsoString(options.executeAt),
2859
+ ...options.payload !== void 0 ? { payload: options.payload } : {},
2860
+ ...options.idempotencyKey !== void 0 ? { idempotencyKey: options.idempotencyKey } : {},
2861
+ ...options.maxAttempts !== void 0 ? { maxAttempts: options.maxAttempts } : {}
2862
+ },
2863
+ authenticated: true
2864
+ });
2865
+ }
2866
+ async get(scheduleId) {
2867
+ return this.http.request({
2868
+ method: "GET",
2869
+ path: `/schedules/${encodeURIComponent(this.projectId)}/${encodeURIComponent(scheduleId)}`,
2870
+ authenticated: true
2871
+ });
2872
+ }
2873
+ async list(options = {}) {
2874
+ return this.http.request({
2875
+ method: "GET",
2876
+ path: `/schedules/${encodeURIComponent(this.projectId)}`,
2877
+ query: {
2878
+ functionId: options.functionId,
2879
+ status: options.status,
2880
+ limit: options.limit,
2881
+ offset: options.offset
2882
+ },
2883
+ authenticated: true
2884
+ });
2885
+ }
2886
+ async cancel(scheduleId) {
2887
+ return this.http.request({
2888
+ method: "DELETE",
2889
+ path: `/schedules/${encodeURIComponent(this.projectId)}/${encodeURIComponent(scheduleId)}`,
2890
+ authenticated: true
2891
+ });
2892
+ }
2893
+ toIsoString(input) {
2894
+ if (input instanceof Date) return input.toISOString();
2895
+ if (typeof input === "number") return new Date(input).toISOString();
2896
+ return input;
2897
+ }
2898
+ };
2899
+
2675
2900
  // libs/sdk/src/client.ts
2676
2901
  function createClient(config) {
2677
2902
  const tokenStorage = config.tokenStorage ?? (typeof window !== "undefined" && typeof window.localStorage !== "undefined" ? new BrowserTokenStorage() : new MemoryTokenStorage());
@@ -2690,12 +2915,14 @@ function createClient(config) {
2690
2915
  const db = new DatabaseModule(httpClient, config.projectId, realtime);
2691
2916
  const notifications = new NotificationsModule(httpClient);
2692
2917
  const functions = new FunctionsModule(httpClient);
2918
+ const schedule = new ScheduleModule(httpClient, config.projectId);
2693
2919
  return {
2694
2920
  auth,
2695
2921
  storage,
2696
2922
  db,
2697
2923
  notifications,
2698
2924
  functions,
2925
+ schedule,
2699
2926
  setTokens(tokens) {
2700
2927
  return tokenManager.setTokens(tokens);
2701
2928
  },
@@ -2722,9 +2949,13 @@ function createClient(config) {
2722
2949
  export {
2723
2950
  BrowserTokenStorage,
2724
2951
  CodeChallengeMethod,
2952
+ CursorInvalidError,
2725
2953
  FileVisibility,
2954
+ ForbiddenError,
2726
2955
  GrantType,
2727
2956
  MemoryTokenStorage,
2957
+ NotFoundError,
2958
+ ServerConfigError,
2728
2959
  SharePermission,
2729
2960
  SpacelrAuthError,
2730
2961
  SpacelrEmailVerificationRequiredError,
@@ -2733,6 +2964,10 @@ export {
2733
2964
  SpacelrSearchFilterRequiredError,
2734
2965
  SpacelrTimeoutError,
2735
2966
  SpacelrTwoFactorRequiredError,
2967
+ TimelineError,
2968
+ TimelineModule,
2969
+ TimeoutError,
2970
+ ValidationError,
2736
2971
  createClient,
2737
2972
  generatePKCEChallenge,
2738
2973
  localStorageCursorStorage,