@objectstack/core 12.5.0 → 13.0.0

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.cjs CHANGED
@@ -54,6 +54,7 @@ __export(index_exports, {
54
54
  SemanticVersionManager: () => SemanticVersionManager,
55
55
  ServiceLifecycle: () => ServiceLifecycle,
56
56
  buildPermissionsFromGrants: () => buildPermissionsFromGrants,
57
+ bulkWrite: () => bulkWrite,
57
58
  calendarPartsInTz: () => calendarPartsInTz,
58
59
  calendarPartsInTzOrUtc: () => calendarPartsInTzOrUtc,
59
60
  counterSignPayload: () => counterSignPayload,
@@ -67,6 +68,7 @@ __export(index_exports, {
67
68
  createPluginConfigValidator: () => createPluginConfigValidator,
68
69
  createPluginPermissionEnforcer: () => createPluginPermissionEnforcer,
69
70
  deepMerge: () => deepMerge,
71
+ defaultIsTransientError: () => defaultIsTransientError,
70
72
  evaluateAuthGate: () => evaluateAuthGate,
71
73
  extractApiKey: () => extractApiKey,
72
74
  generateApiKey: () => generateApiKey,
@@ -90,7 +92,8 @@ __export(index_exports, {
90
92
  verifyPlatformSignature: () => verifyPlatformSignature,
91
93
  verifyPluginArtifact: () => verifyPluginArtifact,
92
94
  verifyPublisherSignature: () => verifyPublisherSignature,
93
- wireAuthoredTranslationSync: () => wireAuthoredTranslationSync
95
+ wireAuthoredTranslationSync: () => wireAuthoredTranslationSync,
96
+ withTransientRetry: () => withTransientRetry
94
97
  });
95
98
  module.exports = __toCommonJS(index_exports);
96
99
 
@@ -4232,7 +4235,7 @@ async function tryFind(ql, object, where, limit = 100) {
4232
4235
  async function resolveAuthzContext(input) {
4233
4236
  const { ql, headers } = input;
4234
4237
  const ctx = {
4235
- roles: [],
4238
+ positions: [],
4236
4239
  permissions: [],
4237
4240
  systemPermissions: [],
4238
4241
  org_user_ids: []
@@ -4281,16 +4284,16 @@ async function resolveAuthzContext(input) {
4281
4284
  if (m.role && typeof m.role === "string") {
4282
4285
  for (const raw of m.role.split(",").map((s) => s.trim()).filter(Boolean)) {
4283
4286
  const r = (0, import_spec.mapMembershipRole)(raw);
4284
- if (!ctx.roles.includes(r)) ctx.roles.push(r);
4287
+ if (!ctx.positions.includes(r)) ctx.positions.push(r);
4285
4288
  }
4286
4289
  }
4287
4290
  }
4288
- const userRoleRows = await tryFind(ql, "sys_user_role", { user_id: userId }, 200);
4289
- for (const ur of userRoleRows) {
4291
+ const userPositionRows = await tryFind(ql, "sys_user_position", { user_id: userId }, 200);
4292
+ for (const ur of userPositionRows) {
4290
4293
  const org = ur.organization_id ?? null;
4291
4294
  if (org && tenantId && org !== tenantId) continue;
4292
- const r = ur.role;
4293
- if (typeof r === "string" && r && !ctx.roles.includes(r)) ctx.roles.push(r);
4295
+ const r = ur.position;
4296
+ if (typeof r === "string" && r && !ctx.positions.includes(r)) ctx.positions.push(r);
4294
4297
  }
4295
4298
  if (tenantId) {
4296
4299
  const orgMembers = await tryFind(ql, "sys_member", { organization_id: tenantId }, 1e3);
@@ -4313,11 +4316,12 @@ async function resolveAuthzContext(input) {
4313
4316
  upsRows.filter((r) => (r.organization_id ?? r.organizationId ?? null) === null).map((r) => r.permission_set_id ?? r.permissionSetId).filter(Boolean)
4314
4317
  );
4315
4318
  let hasPlatformAdminGrant = false;
4316
- if (ctx.roles.length > 0) {
4317
- const roleRows = await tryFind(ql, "sys_role", { name: { $in: ctx.roles } }, 100);
4318
- const roleIds = roleRows.map((r) => r.id).filter(Boolean);
4319
- if (roleIds.length > 0) {
4320
- const rpsRows = await tryFind(ql, "sys_role_permission_set", { role_id: { $in: roleIds } }, 500);
4319
+ if (!ctx.positions.includes("everyone")) ctx.positions.push("everyone");
4320
+ if (ctx.positions.length > 0) {
4321
+ const positionRows = await tryFind(ql, "sys_position", { name: { $in: ctx.positions } }, 100);
4322
+ const positionIds = positionRows.map((r) => r.id).filter(Boolean);
4323
+ if (positionIds.length > 0) {
4324
+ const rpsRows = await tryFind(ql, "sys_position_permission_set", { position_id: { $in: positionIds } }, 500);
4321
4325
  for (const r of rpsRows) {
4322
4326
  const id = r.permission_set_id ?? r.permissionSetId;
4323
4327
  if (id) psIds.add(id);
@@ -4350,8 +4354,8 @@ async function resolveAuthzContext(input) {
4350
4354
  }
4351
4355
  if (Object.keys(mergedTabs).length > 0) ctx.tabPermissions = mergedTabs;
4352
4356
  }
4353
- if (hasPlatformAdminGrant && !ctx.roles.includes(import_spec.BUILTIN_ROLE_PLATFORM_ADMIN)) {
4354
- ctx.roles.unshift(import_spec.BUILTIN_ROLE_PLATFORM_ADMIN);
4357
+ if (hasPlatformAdminGrant && !ctx.positions.includes(import_spec.BUILTIN_IDENTITY_PLATFORM_ADMIN)) {
4358
+ ctx.positions.unshift(import_spec.BUILTIN_IDENTITY_PLATFORM_ADMIN);
4355
4359
  }
4356
4360
  if (!ctx.permissions.includes("ai_seat")) {
4357
4361
  const aiAccess = (await getUserRow())?.ai_access;
@@ -4463,6 +4467,87 @@ function calendarPartsInTzOrUtc(d, tz) {
4463
4467
  };
4464
4468
  }
4465
4469
 
4470
+ // src/utils/bulk-write.ts
4471
+ var DEFAULT_BATCH_SIZE = 200;
4472
+ var DEFAULT_MAX_RETRIES = 3;
4473
+ var DEFAULT_BACKOFF_BASE_MS = 200;
4474
+ var TRANSIENT_PATTERNS = [
4475
+ /fetch failed/i,
4476
+ /network/i,
4477
+ /timed?\s*out/i,
4478
+ /timeout/i,
4479
+ /socket hang ?up/i,
4480
+ /connection.*(closed|reset|refused|terminated|aborted)/i,
4481
+ /\b(502|503|504)\b/,
4482
+ /server.*unavailable/i,
4483
+ /too many connections/i
4484
+ ];
4485
+ var TRANSIENT_CODES = /^(ECONNRESET|ECONNREFUSED|ECONNABORTED|EPIPE|EAI_AGAIN|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|ENOTFOUND)$/i;
4486
+ function defaultIsTransientError(err) {
4487
+ const code = err?.code;
4488
+ if (typeof code === "string" && TRANSIENT_CODES.test(code)) return true;
4489
+ const message = err?.message;
4490
+ const text = typeof message === "string" ? message : String(err ?? "");
4491
+ return TRANSIENT_PATTERNS.some((re) => re.test(text));
4492
+ }
4493
+ var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
4494
+ async function withRetry(fn, opts) {
4495
+ let lastError;
4496
+ for (let attempt = 1; attempt <= opts.maxRetries; attempt++) {
4497
+ try {
4498
+ return await fn();
4499
+ } catch (err) {
4500
+ lastError = err;
4501
+ if (attempt >= opts.maxRetries || !opts.isTransientError(err)) throw err;
4502
+ const jitter = Math.floor(Math.random() * 50);
4503
+ await opts.sleep(opts.backoffBaseMs * 2 ** (attempt - 1) + jitter);
4504
+ }
4505
+ }
4506
+ throw lastError;
4507
+ }
4508
+ async function withTransientRetry(fn, opts = {}) {
4509
+ return withRetry(fn, {
4510
+ maxRetries: Math.max(1, opts.maxRetries ?? DEFAULT_MAX_RETRIES),
4511
+ backoffBaseMs: opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS,
4512
+ isTransientError: opts.isTransientError ?? defaultIsTransientError,
4513
+ sleep: opts.sleep ?? defaultSleep
4514
+ });
4515
+ }
4516
+ async function bulkWrite(rows, opts) {
4517
+ const batchSize = Math.max(1, opts.batchSize ?? DEFAULT_BATCH_SIZE);
4518
+ const retryOpts = {
4519
+ maxRetries: Math.max(1, opts.maxRetries ?? DEFAULT_MAX_RETRIES),
4520
+ backoffBaseMs: opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS,
4521
+ isTransientError: opts.isTransientError ?? defaultIsTransientError,
4522
+ sleep: opts.sleep ?? defaultSleep
4523
+ };
4524
+ const results = new Array(rows.length);
4525
+ for (let start = 0; start < rows.length; start += batchSize) {
4526
+ const batch = rows.slice(start, start + batchSize);
4527
+ try {
4528
+ const records = await withRetry(() => opts.writeBatch(batch), retryOpts);
4529
+ for (let i = 0; i < batch.length; i++) {
4530
+ results[start + i] = { index: start + i, ok: true, record: records[i] };
4531
+ }
4532
+ } catch (batchErr) {
4533
+ if (batch.length === 1) {
4534
+ results[start] = { index: start, ok: false, error: batchErr };
4535
+ continue;
4536
+ }
4537
+ for (let i = 0; i < batch.length; i++) {
4538
+ const idx = start + i;
4539
+ try {
4540
+ const record = await withRetry(() => opts.writeOne(batch[i]), retryOpts);
4541
+ results[idx] = { index: idx, ok: true, record };
4542
+ } catch (err) {
4543
+ results[idx] = { index: idx, ok: false, error: err };
4544
+ }
4545
+ }
4546
+ }
4547
+ }
4548
+ return results;
4549
+ }
4550
+
4466
4551
  // src/health-monitor.ts
4467
4552
  var PluginHealthMonitor = class {
4468
4553
  constructor(logger) {
@@ -5402,6 +5487,7 @@ var NamespaceResolver = class {
5402
5487
  SemanticVersionManager,
5403
5488
  ServiceLifecycle,
5404
5489
  buildPermissionsFromGrants,
5490
+ bulkWrite,
5405
5491
  calendarPartsInTz,
5406
5492
  calendarPartsInTzOrUtc,
5407
5493
  counterSignPayload,
@@ -5415,6 +5501,7 @@ var NamespaceResolver = class {
5415
5501
  createPluginConfigValidator,
5416
5502
  createPluginPermissionEnforcer,
5417
5503
  deepMerge,
5504
+ defaultIsTransientError,
5418
5505
  evaluateAuthGate,
5419
5506
  extractApiKey,
5420
5507
  generateApiKey,
@@ -5438,6 +5525,7 @@ var NamespaceResolver = class {
5438
5525
  verifyPlatformSignature,
5439
5526
  verifyPluginArtifact,
5440
5527
  verifyPublisherSignature,
5441
- wireAuthoredTranslationSync
5528
+ wireAuthoredTranslationSync,
5529
+ withTransientRetry
5442
5530
  });
5443
5531
  //# sourceMappingURL=index.cjs.map