@ai-matrx/data 0.18.34 → 0.18.36

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/db.js CHANGED
@@ -628,28 +628,305 @@ function createOAuthRefreshFetch(args) {
628
628
  return normalizeOAuthResponse(adopted, responseSession, clientId);
629
629
  };
630
630
  }
631
+
632
+ // src/db/write-failure.ts
633
+ var WriteRefusedError = class extends Error {
634
+ status;
635
+ /** What the server said for a person, when it said something a person can read. */
636
+ serverMessage;
637
+ /** The developer line (method, path, status, body) — for diagnostics, never for the screen. */
638
+ technical;
639
+ constructor(args) {
640
+ super(refusalSentence(args.status, args.serverMessage ?? null));
641
+ this.name = "WriteRefusedError";
642
+ this.status = args.status;
643
+ this.serverMessage = args.serverMessage ?? null;
644
+ this.technical = args.technical;
645
+ }
646
+ };
647
+ function serverMessageFromBody(body) {
648
+ if (!body || typeof body !== "object") return null;
649
+ const b = body;
650
+ const detail = b.detail;
651
+ const pick = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : null;
652
+ if (detail && typeof detail === "object" && !Array.isArray(detail)) {
653
+ const d = detail;
654
+ return pick(d.user_message) ?? pick(d.message);
655
+ }
656
+ return pick(b.user_message) ?? pick(detail) ?? pick(b.message);
657
+ }
658
+ var LOOKS_TECHNICAL = /(^|\s)(GET|POST|PUT|PATCH|DELETE)\s+\/|https?:\/\/|\bHTTP \d{3}\b|Traceback|Exception\b|permission denied for|violates|row-level security|\brelation "|\bcolumn "|\bfunction .* does not exist|\bPGRST\d+|\bJWT\b|duplicate key|syntax error|invalid input syntax|JSON object requested|rows returned|\bnull value in column|\bundefined\b|\bat [\w.]+ \(/i;
659
+ function personSentence(text2) {
660
+ if (typeof text2 !== "string") return null;
661
+ let said = text2.trim();
662
+ const named = /^[a-z][a-z0-9_]*[a-z0-9]: (.+)$/s.exec(said);
663
+ if (named) said = (named[1] ?? "").trim().replace(/^[a-z]/, (c) => c.toUpperCase());
664
+ if (said.length < 8 || LOOKS_TECHNICAL.test(said) || !/^[A-Z"“']/.test(said) || !/\s/.test(said)) return null;
665
+ return /[.!?]["”']?$/.test(said) ? said : `${said}.`;
666
+ }
667
+ function isPostgrestShaped(err) {
668
+ if (!err || typeof err !== "object") return false;
669
+ const code = err.code;
670
+ return typeof code === "string" && /^(PGRST\d{3}|[0-9A-Z]{5})$/.test(code) && !("status" in err && typeof err.status === "number");
671
+ }
672
+ function isSerializedRefusal(err) {
673
+ if (!err || typeof err !== "object" || err instanceof Error) return false;
674
+ const e = err;
675
+ if (typeof e.message !== "string" || e.message.trim() === "") return false;
676
+ if (e.name === "WriteRefusedError" || e.name === "WriteDidNotLandError") return true;
677
+ return e.name === "Error" && !LOOKS_TECHNICAL.test(e.message);
678
+ }
679
+ function postgrestSentence(code) {
680
+ if (code === "42501" || code === "PGRST301" || code === "PGRST302") return "You do not have permission to do this.";
681
+ if (code === "PGRST303" || code === "PGRST300") return "Your sign-in has expired. Sign in again, then try again.";
682
+ if (code === "PGRST116") return "It is no longer there \u2014 it may have been deleted.";
683
+ if (code === "23505") return "Something with the same name or key already exists.";
684
+ if (code === "23503") return "It refers to something that no longer exists.";
685
+ if (code === "23514" || code === "23502" || code === "22P02") return "The database would not accept that value.";
686
+ if (code === "57014") return "The database took too long to answer.";
687
+ if (code === "40001" || code === "40P01") return "It changed while you were working. Reload to see the latest.";
688
+ return "The database refused it.";
689
+ }
690
+ function assertWriteLanded(rows, technical) {
691
+ const n = Array.isArray(rows) ? rows.length : rows == null ? 0 : 1;
692
+ if (n > 0) return;
693
+ throw new WriteRefusedError({
694
+ status: 403,
695
+ serverMessage: "Nothing was saved: you may not have permission to change this, or it no longer exists.",
696
+ technical: `${technical} matched 0 rows`
697
+ });
698
+ }
699
+ function refusalSentence(status, serverMessage) {
700
+ const said = serverMessage && !LOOKS_TECHNICAL.test(serverMessage) ? serverMessage : null;
701
+ if (status === 401) return "Your sign-in has expired. Sign in again, then try again.";
702
+ if (status === 403) return said ?? "You do not have permission to do this.";
703
+ if (status === 404) return said ?? "It is no longer there \u2014 it may have been deleted.";
704
+ if (status === 409 || status === 412) return said ?? "It changed while you were working. Reload to see the latest.";
705
+ if (status >= 500) return "The server refused the request.";
706
+ if (said) return `The server said: ${said.replace(/\.?$/, ".")}`;
707
+ return "The server did not accept it.";
708
+ }
709
+ function describeWriteFailure(err, { action, remedy = "Try again." }) {
710
+ const title = `Could not ${action}.`;
711
+ let why;
712
+ if (err instanceof WriteRefusedError) {
713
+ why = err.message;
714
+ } else if (isSerializedRefusal(err)) {
715
+ why = /[.!?]["”']?$/.test(err.message.trim()) ? err.message.trim() : `${err.message.trim()}.`;
716
+ } else if (isPostgrestShaped(err)) {
717
+ why = personSentence(err.message) ?? postgrestSentence(err.code);
718
+ } else if (err && typeof err === "object" && "status" in err && typeof err.status === "number") {
719
+ const e = err;
720
+ why = refusalSentence(e.status, typeof e.userMessage === "string" ? e.userMessage : null);
721
+ } else if (err instanceof TypeError || err instanceof Error && /Failed to fetch|NetworkError|Load failed/i.test(err.message)) {
722
+ why = "The server could not be reached. Check your connection.";
723
+ } else if (err instanceof Error && err.message && !LOOKS_TECHNICAL.test(err.message)) {
724
+ why = err.message.replace(/\.?$/, ".");
725
+ } else if (typeof err === "string" && err.trim() && !LOOKS_TECHNICAL.test(err)) {
726
+ why = err.trim().replace(/\.?$/, ".");
727
+ } else {
728
+ why = "Something went wrong on our side.";
729
+ }
730
+ return { title, description: `${why} ${remedy}`.trim() };
731
+ }
732
+
733
+ // src/db/write-one.ts
734
+ import { PostgrestError } from "@supabase/supabase-js";
735
+ var PAST = {
736
+ save: "saved",
737
+ update: "updated",
738
+ rename: "renamed",
739
+ move: "moved",
740
+ delete: "deleted",
741
+ remove: "removed",
742
+ archive: "archived",
743
+ restore: "restored",
744
+ publish: "published",
745
+ unpublish: "unpublished",
746
+ change: "changed"
747
+ };
748
+ var GERUND = {
749
+ save: "saving",
750
+ update: "updating",
751
+ rename: "renaming",
752
+ move: "moving",
753
+ delete: "deleting",
754
+ remove: "removing",
755
+ archive: "archiving",
756
+ restore: "restoring",
757
+ publish: "publishing",
758
+ unpublish: "unpublishing",
759
+ change: "changing"
760
+ };
761
+ var WriteDidNotLandError = class extends WriteRefusedError {
762
+ action;
763
+ noun;
764
+ reason;
765
+ constructor({ action, noun }, reason = "refused") {
766
+ super({
767
+ status: reason === "taken" ? 409 : 403,
768
+ serverMessage: reason === "taken" ? `This ${noun} was already handled \u2014 refresh to see the latest.` : `Nothing was ${PAST[action]}: this ${noun} no longer exists, or your access does not allow ${GERUND[action]} it.`,
769
+ technical: reason === "taken" ? `writeOne(${action} ${noun}) compare-and-set matched 0 rows \u2014 another writer got there first` : `writeOne(${action} ${noun}) matched 0 rows \u2014 RLS refused or the row is gone`
770
+ });
771
+ this.name = "WriteDidNotLandError";
772
+ this.action = action;
773
+ this.noun = noun;
774
+ this.reason = reason;
775
+ }
776
+ };
777
+ async function writeOne(write, options) {
778
+ const { data, error } = await write;
779
+ if (error) throw error;
780
+ if (Array.isArray(data) && data.length > 0) return data[0];
781
+ if (options.alreadyDone) {
782
+ const current = await options.alreadyDone.reread();
783
+ if (current.error) throw current.error;
784
+ const row = Array.isArray(current.data) ? current.data[0] : current.data;
785
+ if (row && options.alreadyDone.isDone(row)) return row;
786
+ }
787
+ throw new WriteDidNotLandError(options, options.compareAndSet ? "taken" : "refused");
788
+ }
789
+ async function tryWriteOne(write, options) {
790
+ try {
791
+ return { row: await writeOne(write, options), error: null };
792
+ } catch (caught) {
793
+ if (caught instanceof Error) return { row: null, error: caught };
794
+ if (caught && typeof caught === "object" && "code" in caught && typeof caught.code === "string" && "message" in caught && typeof caught.message === "string") {
795
+ return { row: null, error: new PostgrestError({
796
+ code: caught.code,
797
+ message: caught.message,
798
+ details: "details" in caught && typeof caught.details === "string" ? caught.details : "",
799
+ hint: "hint" in caught && typeof caught.hint === "string" ? caught.hint : ""
800
+ }) };
801
+ }
802
+ const message = caught && typeof caught === "object" && "message" in caught && typeof caught.message === "string" ? caught.message : String(caught);
803
+ const error = new Error(message);
804
+ error.cause = caught;
805
+ return { row: null, error };
806
+ }
807
+ }
808
+ function writeFailureStatus(error) {
809
+ if (error instanceof WriteDidNotLandError) return error.status;
810
+ if (error && typeof error === "object" && "code" in error) {
811
+ if (error.code === "42501") return 403;
812
+ if (error.code === "PGRST301" || error.code === "PGRST302" || error.code === "PGRST303") return 401;
813
+ if (error.code === "PGRST116") return 404;
814
+ if (error.code === "23505" || error.code === "40001" || error.code === "40P01") return 409;
815
+ }
816
+ return 500;
817
+ }
818
+ var BulkWriteError = class extends Error {
819
+ failures;
820
+ attempted;
821
+ constructor(failures, attempted) {
822
+ super(
823
+ `${failures.length} of ${attempted} ${attempted === 1 ? "change was" : "changes were"} not saved. ${failures[0]?.message ?? ""}`.trim()
824
+ );
825
+ this.name = "BulkWriteError";
826
+ this.failures = failures;
827
+ this.attempted = attempted;
828
+ }
829
+ };
830
+ async function writeOneRow(write, options) {
831
+ const { data, error } = await write;
832
+ if (error) {
833
+ if (error instanceof PostgrestError) return { data: null, error };
834
+ const e = error;
835
+ return {
836
+ data: null,
837
+ error: new PostgrestError({
838
+ message: typeof e.message === "string" ? e.message : String(error),
839
+ details: typeof e.details === "string" ? e.details : "",
840
+ hint: typeof e.hint === "string" ? e.hint : "",
841
+ code: typeof e.code === "string" ? e.code : ""
842
+ })
843
+ };
844
+ }
845
+ const rows = Array.isArray(data) ? data : [];
846
+ if (rows.length === 1) return { data: rows[0], error: null };
847
+ const refusal = new WriteDidNotLandError(options, options.compareAndSet ? "taken" : "refused");
848
+ return {
849
+ data: null,
850
+ error: new PostgrestError({
851
+ message: rows.length === 0 ? refusal.serverMessage ?? refusal.message : `${rows.length} records matched where one ${options.noun} was expected, and all of them were changed. Reload to see the result.`,
852
+ details: rows.length === 0 ? refusal.technical : `writeOneRow(${options.action} ${options.noun}) matched ${rows.length} rows`,
853
+ hint: "",
854
+ code: "PGRST116"
855
+ })
856
+ };
857
+ }
858
+
859
+ // src/db/record-unavailable.ts
860
+ var RecordUnavailableError = class extends Error {
861
+ entity;
862
+ reason;
863
+ recordId;
864
+ /**
865
+ * Canonical entity token, when the read site knows it. With a token AND a
866
+ * recordId the renderer stops describing the ambiguity and ASKS the platform
867
+ * instead — an access gate resolves which of the four it really is.
868
+ * Without one, honest ambiguity is still the best available answer.
869
+ */
870
+ token;
871
+ /** Diagnostics identity for reconciling this exact capture in place. */
872
+ captureId;
873
+ /** Last truth written to diagnostics; avoids duplicate reconciliation. */
874
+ captureResolution;
875
+ constructor(input) {
876
+ super(recordUnavailableMessage(input.entity, input.reason));
877
+ this.name = "RecordUnavailableError";
878
+ this.entity = input.entity;
879
+ this.reason = input.reason;
880
+ this.recordId = input.recordId;
881
+ this.token = input.token;
882
+ this.captureResolution = input.reason;
883
+ }
884
+ };
885
+ function recordUnavailableMessage(entity, reason) {
886
+ if (reason === "deleted") {
887
+ return `This ${entity} was deleted, so it can no longer be opened.`;
888
+ }
889
+ return `We couldn't open this ${entity}. It may have been deleted, or it may belong to an organization you don't have access to.`;
890
+ }
891
+ function isRecordUnavailableError(value) {
892
+ return value instanceof RecordUnavailableError;
893
+ }
631
894
  export {
895
+ BulkWriteError,
632
896
  DEFAULT_ACTIVE_ORG_COOKIE_MAX_AGE,
633
897
  DEFAULT_ACTIVE_ORG_COOKIE_NAME,
634
898
  DEFAULT_AUTH_COOKIE_NAME,
635
899
  IncompleteReadError,
900
+ RecordUnavailableError,
636
901
  SessionUnavailableError,
902
+ WriteDidNotLandError,
903
+ WriteRefusedError,
637
904
  asJsonObject,
905
+ assertWriteLanded,
638
906
  createActiveOrgCookie,
639
907
  createAuthCookie,
640
908
  createOAuthRefreshFetch,
641
909
  createSessionRetry,
642
910
  decodeActiveOrgSelection,
643
911
  decodeOAuthRefreshClaims,
912
+ describeWriteFailure,
644
913
  encodeActiveOrgSelection,
645
914
  guardedUpdate,
646
915
  isMissingSessionError,
916
+ isRecordUnavailableError,
647
917
  mergeJsonColumn,
648
918
  parseCookieHeader,
919
+ personSentence,
649
920
  readAllRows,
650
921
  readAllRowsRest,
922
+ recordUnavailableMessage,
923
+ serverMessageFromBody,
651
924
  tryReadAllRows,
652
925
  tryReadAllRowsRest,
653
- uniqueChannelTopic
926
+ tryWriteOne,
927
+ uniqueChannelTopic,
928
+ writeFailureStatus,
929
+ writeOne,
930
+ writeOneRow
654
931
  };
655
932
  //# sourceMappingURL=db.js.map