@raisindb/client 0.4.0 → 0.4.2

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.js CHANGED
@@ -60,6 +60,7 @@ __export(src_exports, {
60
60
  HttpFunctionsApi: () => HttpFunctionsApi,
61
61
  HttpNodeTypes: () => HttpNodeTypes,
62
62
  HttpWorkspaceClient: () => HttpWorkspaceClient,
63
+ IdentityAuthApi: () => IdentityAuthApi,
63
64
  InboxApi: () => InboxApi,
64
65
  LocalStorageTokenStorage: () => LocalStorageTokenStorage,
65
66
  LogLevel: () => LogLevel,
@@ -135,6 +136,7 @@ __export(src_exports, {
135
136
  logger: () => logger,
136
137
  normalizeHomePath: () => normalizeHomePath,
137
138
  projectPlansFromMessages: () => projectPlansFromMessages,
139
+ readTokensFromFragment: () => readTokensFromFragment,
138
140
  rowsToObjects: () => rowsToObjects,
139
141
  setLogLevel: () => setLogLevel,
140
142
  sleep: () => sleep,
@@ -3148,6 +3150,13 @@ var Branches = class {
3148
3150
  * Each root's parent path must already exist on the target branch. With
3149
3151
  * `deleteMissing`, target nodes under the roots that are absent from the
3150
3152
  * copied source set are pruned (one-way sync).
3153
+ *
3154
+ * `sourceRevision` pins the read to a point in time. A promotion of a large
3155
+ * set is not instantaneous, so without it the copy reads each node when its
3156
+ * turn comes and a writer working in parallel lands partly inside the result
3157
+ * — a torn snapshot. Pass the revision captured when the operation was
3158
+ * decided (see `getHead`) to copy exactly that state. The target branch is
3159
+ * always resolved at its head: the pin says WHAT to copy, never where.
3151
3160
  */
3152
3161
  async copyNodes(sourceBranch, targetBranch, opts) {
3153
3162
  return this.sendRequest(
@@ -3157,7 +3166,8 @@ var Branches = class {
3157
3166
  workspace: opts.workspace,
3158
3167
  roots: opts.roots,
3159
3168
  recursive: opts.recursive ?? true,
3160
- delete_missing: opts.deleteMissing ?? false
3169
+ delete_missing: opts.deleteMissing ?? false,
3170
+ source_revision: opts.sourceRevision ?? null
3161
3171
  },
3162
3172
  "branch_copy_nodes" /* BranchCopyNodes */
3163
3173
  );
@@ -7031,6 +7041,101 @@ var McpClient = class {
7031
7041
  }
7032
7042
  };
7033
7043
 
7044
+ // src/identity-auth.ts
7045
+ var IdentityAuthApi = class {
7046
+ constructor(repository, transport) {
7047
+ this.repository = repository;
7048
+ this.transport = transport;
7049
+ }
7050
+ get base() {
7051
+ return `/auth/${encodeURIComponent(this.repository)}`;
7052
+ }
7053
+ /**
7054
+ * Ask RaisinDB to email a sign-in link.
7055
+ *
7056
+ * The reply is an acknowledgement, never a token — the token only exists in
7057
+ * the email. It also resolves for an address that has no account, because a
7058
+ * response that differed would turn this endpoint into an account-existence
7059
+ * oracle.
7060
+ */
7061
+ async sendMagicLink(email, options) {
7062
+ return this.transport({
7063
+ method: "POST",
7064
+ path: `${this.base}/magic-link`,
7065
+ body: { email, redirect_url: options?.redirectUrl },
7066
+ skipAuth: true
7067
+ });
7068
+ }
7069
+ /**
7070
+ * Exchange a magic-link token for a session.
7071
+ *
7072
+ * Most applications never call this: following the emailed link hits
7073
+ * RaisinDB directly, which verifies and 302s to the redirect URL with the
7074
+ * tokens in the URL **fragment** (so they stay out of server logs and out
7075
+ * of the Referer header) — see {@link readTokensFromFragment}. Call this
7076
+ * only when handling the token yourself, and note it is single-use.
7077
+ */
7078
+ async verifyMagicLink(token) {
7079
+ return this.transport({
7080
+ method: "GET",
7081
+ path: `${this.base}/magic-link/verify?token=${encodeURIComponent(token)}`,
7082
+ skipAuth: true
7083
+ });
7084
+ }
7085
+ /** Sign in with email and password, where the repository enables it. */
7086
+ async login(email, password, options) {
7087
+ return this.transport({
7088
+ method: "POST",
7089
+ path: `${this.base}/login`,
7090
+ body: { email, password, remember_me: options?.rememberMe ?? false },
7091
+ skipAuth: true
7092
+ });
7093
+ }
7094
+ /** Create an account with email and password. */
7095
+ async register(email, password, options) {
7096
+ return this.transport({
7097
+ method: "POST",
7098
+ path: `${this.base}/register`,
7099
+ body: { email, password, display_name: options?.displayName },
7100
+ skipAuth: true
7101
+ });
7102
+ }
7103
+ /** Trade a refresh token for a fresh pair. */
7104
+ async refresh(refreshToken) {
7105
+ return this.transport({
7106
+ method: "POST",
7107
+ path: `${this.base}/refresh`,
7108
+ body: { refresh_token: refreshToken },
7109
+ skipAuth: true
7110
+ });
7111
+ }
7112
+ /**
7113
+ * Which sign-in methods to render.
7114
+ *
7115
+ * Ask rather than hard-code: magic link and password are per-repository
7116
+ * configuration, and a button for a disabled method is a dead end.
7117
+ */
7118
+ async providers() {
7119
+ return this.transport({
7120
+ method: "GET",
7121
+ path: `${this.base}/providers`,
7122
+ skipAuth: true
7123
+ });
7124
+ }
7125
+ /** Who the current token belongs to in this repository. */
7126
+ async me() {
7127
+ return this.transport({ method: "GET", path: `${this.base}/me` });
7128
+ }
7129
+ };
7130
+ function readTokensFromFragment(hashOrUrl) {
7131
+ const hash = hashOrUrl.includes("#") ? hashOrUrl.slice(hashOrUrl.indexOf("#") + 1) : hashOrUrl;
7132
+ if (!hash) return null;
7133
+ const params = new URLSearchParams(hash);
7134
+ const accessToken = params.get("access_token");
7135
+ if (!accessToken) return null;
7136
+ return { accessToken, refreshToken: params.get("refresh_token") };
7137
+ }
7138
+
7034
7139
  // src/http-client.ts
7035
7140
  var RaisinHttpClient = class extends import_events3.EventEmitter {
7036
7141
  constructor(baseUrl, options = {}) {
@@ -7429,6 +7534,65 @@ var RaisinHttpClient = class extends import_events3.EventEmitter {
7429
7534
  return this.authManager;
7430
7535
  }
7431
7536
  // ============================================================================
7537
+ // Identity Authentication
7538
+ // ============================================================================
7539
+ /**
7540
+ * Identity authentication (magic link, password, providers) for one
7541
+ * repository.
7542
+ *
7543
+ * Identities are per repository — their home nodes and roles live there — so
7544
+ * the repository is required rather than inferred from client state, which
7545
+ * would silently sign somebody into whichever one happened to be current.
7546
+ *
7547
+ * ```ts
7548
+ * const auth = client.auth('studio');
7549
+ * await auth.sendMagicLink('a@example.com', {
7550
+ * redirectUrl: 'https://app.example.com/account/callback',
7551
+ * });
7552
+ * // …on the callback route:
7553
+ * const tokens = readTokensFromFragment(window.location.hash);
7554
+ * if (tokens) client.setIdentityTokens(tokens.accessToken, tokens.refreshToken);
7555
+ * ```
7556
+ */
7557
+ auth(repository) {
7558
+ return new IdentityAuthApi(repository, async (options) => {
7559
+ const response = await this.request({
7560
+ method: options.method,
7561
+ path: options.path,
7562
+ body: options.body,
7563
+ skipAuth: options.skipAuth
7564
+ });
7565
+ return response.data;
7566
+ });
7567
+ }
7568
+ /**
7569
+ * Adopt an identity's tokens for every subsequent call on this client.
7570
+ *
7571
+ * Takes the loose form so it serves both callers: the whole result of a
7572
+ * sign-in, and the pair recovered from a redirect fragment (which carries no
7573
+ * identity object and may carry no refresh token).
7574
+ */
7575
+ setIdentityTokens(accessTokenOrResult, refreshToken) {
7576
+ if (typeof accessTokenOrResult === "string") {
7577
+ this.authManager.storage.setAccessToken(accessTokenOrResult);
7578
+ if (refreshToken) this.authManager.storage.setRefreshToken(refreshToken);
7579
+ return;
7580
+ }
7581
+ const result = accessTokenOrResult;
7582
+ this.authManager.setTokens({
7583
+ access_token: result.access_token,
7584
+ refresh_token: result.refresh_token,
7585
+ // The server reports an absolute expiry; AuthManager schedules from a
7586
+ // duration. Deriving it here keeps the refresh timer honest instead of
7587
+ // leaving it unset, which reads as "never expires".
7588
+ expires_in: Math.max(0, result.expires_at - Math.floor(Date.now() / 1e3))
7589
+ });
7590
+ }
7591
+ /** Forget the current identity's tokens. */
7592
+ clearIdentityTokens() {
7593
+ this.authManager.clear();
7594
+ }
7595
+ // ============================================================================
7432
7596
  // Upload Methods
7433
7597
  // ============================================================================
7434
7598
  /**
@@ -12309,6 +12473,7 @@ function createRaisinVue(vue) {
12309
12473
  HttpFunctionsApi,
12310
12474
  HttpNodeTypes,
12311
12475
  HttpWorkspaceClient,
12476
+ IdentityAuthApi,
12312
12477
  InboxApi,
12313
12478
  LocalStorageTokenStorage,
12314
12479
  LogLevel,
@@ -12384,6 +12549,7 @@ function createRaisinVue(vue) {
12384
12549
  logger,
12385
12550
  normalizeHomePath,
12386
12551
  projectPlansFromMessages,
12552
+ readTokensFromFragment,
12387
12553
  rowsToObjects,
12388
12554
  setLogLevel,
12389
12555
  sleep,