@granular-software/sdk 0.4.61 → 0.4.63

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.
@@ -14736,6 +14736,15 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14736
14736
  is_final: Boolean!
14737
14737
  history: [StateMachineTransitionEvent!]!
14738
14738
  instances_in_state(state: String!): [Model!]!
14739
+ instance_population(state: String!, page: Int = 1, per_page: Int = 50): StateMachineInstancePopulation!
14740
+ }
14741
+
14742
+ type StateMachineInstancePopulation {
14743
+ count: Int!
14744
+ instances: [Model!]!
14745
+ page: Int!
14746
+ per_page: Int!
14747
+ has_more: Boolean!
14739
14748
  }
14740
14749
 
14741
14750
  type StateMachineState {
@@ -14944,6 +14953,13 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14944
14953
  value.model.target || value.model,
14945
14954
  value.name,
14946
14955
  state
14956
+ ),
14957
+ instance_population: async (value, { state, page, per_page }) => await stateMachines.instancePopulation(
14958
+ value.model.target || value.model,
14959
+ value.name,
14960
+ state,
14961
+ page,
14962
+ per_page
14947
14963
  )
14948
14964
  }
14949
14965
  };
@@ -15192,7 +15208,6 @@ function buildEffectMetamodelMutations(toolPath, spec) {
15192
15208
  // src/client.ts
15193
15209
  var DEFAULT_CONVERSATION_SESSION_LIST_LIMIT = 100;
15194
15210
  var MAX_CONVERSATION_SESSION_LIST_LIMIT = 500;
15195
- var MAX_CONVERSATION_SESSION_LIST_OFFSET = 1e5;
15196
15211
  function requireUserEnvironmentSequence(value, field) {
15197
15212
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
15198
15213
  throw new Error(
@@ -15572,6 +15587,10 @@ var Environment = class _Environment {
15572
15587
  get sessions() {
15573
15588
  return {
15574
15589
  list: async (options = {}) => this.listSessions(options),
15590
+ page: async (options = {}) => this.granular.listSessionsPage({
15591
+ ...options,
15592
+ environmentId: this.environmentId
15593
+ }),
15575
15594
  create: async (options) => this.createSession(options),
15576
15595
  connect: async (sessionId, options) => this.connectSession(sessionId, options),
15577
15596
  reopen: async (sessionId, options) => this.reopenSession(sessionId, options),
@@ -15827,7 +15846,6 @@ var Environment = class _Environment {
15827
15846
  headers: {
15828
15847
  Authorization: `Bearer ${this._apiKey}`,
15829
15848
  "Content-Type": "application/json",
15830
- Connection: "close",
15831
15849
  ...options.headers
15832
15850
  }
15833
15851
  });
@@ -17351,8 +17369,7 @@ var EnvironmentSession = class extends Session {
17351
17369
  method: "POST",
17352
17370
  headers: {
17353
17371
  "Content-Type": "application/json",
17354
- Authorization: `Bearer ${this.environment.authToken}`,
17355
- Connection: "close"
17372
+ Authorization: `Bearer ${this.environment.authToken}`
17356
17373
  },
17357
17374
  body: JSON.stringify({
17358
17375
  reason: "sdk_disconnect_http_fallback",
@@ -17880,11 +17897,16 @@ var Granular = class _Granular {
17880
17897
  `${methodName}() requires at least one permission so the SDK can ensure assignments for new users.`
17881
17898
  );
17882
17899
  }
17883
- for (const profileName of user.permissions) {
17884
- const profileId = await this.ensurePermissionProfile(
17885
- sandbox.sandboxId,
17886
- profileName
17900
+ const permissionProfileIds = [];
17901
+ for (const profileReference of user.permissions) {
17902
+ permissionProfileIds.push(
17903
+ await this.ensurePermissionProfile(
17904
+ sandbox.sandboxId,
17905
+ profileReference
17906
+ )
17887
17907
  );
17908
+ }
17909
+ for (const profileId of permissionProfileIds) {
17888
17910
  await this.ensureAssignment(
17889
17911
  user.granularId,
17890
17912
  sandbox.sandboxId,
@@ -18049,6 +18071,21 @@ var Granular = class _Granular {
18049
18071
  * List indexed sessions using ownership filters and bounded pagination.
18050
18072
  */
18051
18073
  async listSessions(options) {
18074
+ const items = [];
18075
+ let cursor = options.cursor?.trim() || void 0;
18076
+ let pageCount = 0;
18077
+ do {
18078
+ const page = await this.listSessionsPage({ ...options, cursor });
18079
+ items.push(...page.items);
18080
+ cursor = page.nextCursor || void 0;
18081
+ pageCount += 1;
18082
+ if (pageCount > 1e4) {
18083
+ throw new Error("Session pagination exceeded the safe page limit");
18084
+ }
18085
+ } while (cursor);
18086
+ return items;
18087
+ }
18088
+ async listSessionsPage(options) {
18052
18089
  const environmentId = options.environmentId?.trim();
18053
18090
  const sandboxId = options.sandboxId?.trim();
18054
18091
  const subjectId = options.subjectId?.trim();
@@ -18076,17 +18113,14 @@ var Granular = class _Granular {
18076
18113
  1,
18077
18114
  MAX_CONVERSATION_SESSION_LIST_LIMIT
18078
18115
  );
18079
- const offset = boundedSessionListInteger(
18080
- options.offset,
18081
- "offset",
18082
- 0,
18083
- 0,
18084
- MAX_CONVERSATION_SESSION_LIST_OFFSET
18085
- );
18116
+ const cursor = options.cursor?.trim();
18117
+ if (options.cursor !== void 0 && !cursor) {
18118
+ throw new Error("Session list cursor must be a non-empty string.");
18119
+ }
18086
18120
  const query = new URLSearchParams({
18087
- limit: String(limit),
18088
- offset: String(offset)
18121
+ limit: String(limit)
18089
18122
  });
18123
+ if (cursor) query.set("cursor", cursor);
18090
18124
  if (environmentId) query.set("environmentId", environmentId);
18091
18125
  if (sandboxId) query.set("sandboxId", sandboxId);
18092
18126
  if (subjectId) query.set("userId", subjectId);
@@ -18094,11 +18128,13 @@ var Granular = class _Granular {
18094
18128
  query.set("sessionScope", options.sessionScope.trim());
18095
18129
  }
18096
18130
  if (status !== "all") query.set("status", status);
18097
- const res = await this.request(
18098
- `/control/sessions?${query.toString()}`
18099
- );
18100
- const items = Array.isArray(res.items) ? res.items : [];
18101
- return items.map((row) => this.normalizeConversationSession(row));
18131
+ const res = await this.request(`/control/sessions?${query.toString()}`);
18132
+ const items = Array.isArray(res.items) ? res.items.map((row) => this.normalizeConversationSession(row)) : [];
18133
+ const nextCursor = res.nextCursor ?? null;
18134
+ if (nextCursor === cursor) {
18135
+ throw new Error("Session pagination cursor did not advance");
18136
+ }
18137
+ return { items, nextCursor };
18102
18138
  }
18103
18139
  /**
18104
18140
  * List active (open) sessions for an environment.
@@ -18125,9 +18161,6 @@ var Granular = class _Granular {
18125
18161
  if (typeof options.limit === "number") {
18126
18162
  query.set("limit", String(options.limit));
18127
18163
  }
18128
- if (typeof options.offset === "number") {
18129
- query.set("offset", String(options.offset));
18130
- }
18131
18164
  const state = await this.request(
18132
18165
  `/sdk/user-environment-state?${query.toString()}`
18133
18166
  );
@@ -18889,25 +18922,37 @@ var Granular = class _Granular {
18889
18922
  }
18890
18923
  }
18891
18924
  /**
18892
- * Ensure a permission profile exists for a sandbox, creating it if needed.
18893
- * If profileName matches an existing profile name, returns its ID.
18894
- * Otherwise, creates a v1 source-profile file shape with an allow default.
18925
+ * Resolve an existing permission profile by exact name or ID.
18926
+ *
18927
+ * `allow-all` is the sole compatibility profile that the SDK may provision
18928
+ * automatically. Every other missing reference fails closed so a typo or
18929
+ * untrusted role value cannot become an allow-default profile.
18895
18930
  */
18896
- async ensurePermissionProfile(sandboxId, profileName) {
18897
- try {
18898
- const profiles = await this.permissionProfiles.list(sandboxId);
18899
- const existing = profiles.find((p) => p.name === profileName);
18900
- if (existing) {
18901
- return existing.permissionProfileId;
18902
- }
18903
- } catch {
18931
+ async ensurePermissionProfile(sandboxId, profileReference) {
18932
+ const normalizedReference = typeof profileReference === "string" ? profileReference.trim() : "";
18933
+ if (!normalizedReference) {
18934
+ throw new Error(
18935
+ "Permission profile references must be non-empty names or IDs."
18936
+ );
18937
+ }
18938
+ const profiles = await this.permissionProfiles.list(sandboxId);
18939
+ const existing = profiles.find(
18940
+ (profile) => profile.name === normalizedReference || profile.permissionProfileId === normalizedReference
18941
+ );
18942
+ if (existing) {
18943
+ return existing.permissionProfileId;
18944
+ }
18945
+ if (normalizedReference !== "allow-all") {
18946
+ throw new Error(
18947
+ `Permission profile "${normalizedReference}" was not found in sandbox ${sandboxId}. Provision it with granular build or granular deploy before calling openEnvironment(). Only the reserved "allow-all" profile can be created automatically.`
18948
+ );
18904
18949
  }
18905
18950
  const created = await this.permissionProfiles.create(sandboxId, {
18906
- name: profileName,
18951
+ name: normalizedReference,
18907
18952
  rules: {
18908
18953
  schemaVersion: 1,
18909
- name: profileName,
18910
- description: profileName === "allow-all" ? "Every declared action is visible unless a manifest policy denies it." : `Generated permission profile ${profileName}`,
18954
+ name: normalizedReference,
18955
+ description: "Every declared action is visible unless a manifest policy denies it.",
18911
18956
  defaults: { actionPolicy: "allow" },
18912
18957
  actions: []
18913
18958
  }
@@ -19049,10 +19094,20 @@ var Granular = class _Granular {
19049
19094
  get environments() {
19050
19095
  return {
19051
19096
  list: async (sandboxId) => {
19052
- const result = await this.request(
19053
- `/control/sandboxes/${sandboxId}/environments`
19054
- );
19055
- return result.items.map(normalizeEnvironmentData);
19097
+ const environments = [];
19098
+ let cursor = null;
19099
+ do {
19100
+ const query = cursor ? `?limit=100&cursor=${encodeURIComponent(cursor)}` : "";
19101
+ const result = await this.request(
19102
+ `/control/sandboxes/${sandboxId}/environments${query}`
19103
+ );
19104
+ environments.push(...result.items.map(normalizeEnvironmentData));
19105
+ if (result.nextCursor && result.nextCursor === cursor) {
19106
+ throw new Error("Environment pagination cursor did not advance");
19107
+ }
19108
+ cursor = result.nextCursor;
19109
+ } while (cursor);
19110
+ return environments;
19056
19111
  },
19057
19112
  get: async (environmentId) => {
19058
19113
  return normalizeEnvironmentData(
@@ -19262,7 +19317,6 @@ var Granular = class _Granular {
19262
19317
  headers: {
19263
19318
  Authorization: `Bearer ${this.apiKey}`,
19264
19319
  "Content-Type": "application/json",
19265
- Connection: "close",
19266
19320
  ...options.headers
19267
19321
  }
19268
19322
  });