@ai-matrx/data 0.18.34 → 0.18.35
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/CHANGELOG.md +11 -0
- package/dist/db.cjs +239 -1
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +201 -1
- package/dist/db.d.ts +201 -1
- package/dist/db.js +239 -1
- package/dist/db.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.18.35
|
|
4
|
+
### Unreleased
|
|
5
|
+
|
|
6
|
+
- `/db` gains `writeOne`, `tryWriteOne`, `writeOneRow`, `writeFailureStatus`, `WriteDidNotLandError`,
|
|
7
|
+
`BulkWriteError` and the write-failure words (`WriteRefusedError`, `describeWriteFailure`,
|
|
8
|
+
`assertWriteLanded`, `personSentence`, `serverMessageFromBody`) — moved verbatim from
|
|
9
|
+
matrx-frontend so `@ai-matrx/chat` stops reaching into the app for them.
|
|
10
|
+
- `@supabase/supabase-js` is now a peer dependency (external, never bundled) so `PostgrestError`
|
|
11
|
+
is the consumer's own class and `instanceof` checks hold.
|
|
12
|
+
- **Consumer action:** none required; matrx-frontend's old modules re-export these.
|
|
13
|
+
|
|
3
14
|
## 0.18.34
|
|
4
15
|
|
|
5
16
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
package/dist/db.cjs
CHANGED
|
@@ -20,28 +20,39 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/db.ts
|
|
21
21
|
var db_exports = {};
|
|
22
22
|
__export(db_exports, {
|
|
23
|
+
BulkWriteError: () => BulkWriteError,
|
|
23
24
|
DEFAULT_ACTIVE_ORG_COOKIE_MAX_AGE: () => DEFAULT_ACTIVE_ORG_COOKIE_MAX_AGE,
|
|
24
25
|
DEFAULT_ACTIVE_ORG_COOKIE_NAME: () => DEFAULT_ACTIVE_ORG_COOKIE_NAME,
|
|
25
26
|
DEFAULT_AUTH_COOKIE_NAME: () => DEFAULT_AUTH_COOKIE_NAME,
|
|
26
27
|
IncompleteReadError: () => IncompleteReadError,
|
|
27
28
|
SessionUnavailableError: () => SessionUnavailableError,
|
|
29
|
+
WriteDidNotLandError: () => WriteDidNotLandError,
|
|
30
|
+
WriteRefusedError: () => WriteRefusedError,
|
|
28
31
|
asJsonObject: () => asJsonObject,
|
|
32
|
+
assertWriteLanded: () => assertWriteLanded,
|
|
29
33
|
createActiveOrgCookie: () => createActiveOrgCookie,
|
|
30
34
|
createAuthCookie: () => createAuthCookie,
|
|
31
35
|
createOAuthRefreshFetch: () => createOAuthRefreshFetch,
|
|
32
36
|
createSessionRetry: () => createSessionRetry,
|
|
33
37
|
decodeActiveOrgSelection: () => decodeActiveOrgSelection,
|
|
34
38
|
decodeOAuthRefreshClaims: () => decodeOAuthRefreshClaims,
|
|
39
|
+
describeWriteFailure: () => describeWriteFailure,
|
|
35
40
|
encodeActiveOrgSelection: () => encodeActiveOrgSelection,
|
|
36
41
|
guardedUpdate: () => guardedUpdate,
|
|
37
42
|
isMissingSessionError: () => isMissingSessionError,
|
|
38
43
|
mergeJsonColumn: () => mergeJsonColumn,
|
|
39
44
|
parseCookieHeader: () => parseCookieHeader,
|
|
45
|
+
personSentence: () => personSentence,
|
|
40
46
|
readAllRows: () => readAllRows,
|
|
41
47
|
readAllRowsRest: () => readAllRowsRest,
|
|
48
|
+
serverMessageFromBody: () => serverMessageFromBody,
|
|
42
49
|
tryReadAllRows: () => tryReadAllRows,
|
|
43
50
|
tryReadAllRowsRest: () => tryReadAllRowsRest,
|
|
44
|
-
|
|
51
|
+
tryWriteOne: () => tryWriteOne,
|
|
52
|
+
uniqueChannelTopic: () => import_realtime.uniqueChannelTopic,
|
|
53
|
+
writeFailureStatus: () => writeFailureStatus,
|
|
54
|
+
writeOne: () => writeOne,
|
|
55
|
+
writeOneRow: () => writeOneRow
|
|
45
56
|
});
|
|
46
57
|
module.exports = __toCommonJS(db_exports);
|
|
47
58
|
|
|
@@ -675,4 +686,231 @@ function createOAuthRefreshFetch(args) {
|
|
|
675
686
|
return normalizeOAuthResponse(adopted, responseSession, clientId);
|
|
676
687
|
};
|
|
677
688
|
}
|
|
689
|
+
|
|
690
|
+
// src/db/write-failure.ts
|
|
691
|
+
var WriteRefusedError = class extends Error {
|
|
692
|
+
status;
|
|
693
|
+
/** What the server said for a person, when it said something a person can read. */
|
|
694
|
+
serverMessage;
|
|
695
|
+
/** The developer line (method, path, status, body) — for diagnostics, never for the screen. */
|
|
696
|
+
technical;
|
|
697
|
+
constructor(args) {
|
|
698
|
+
super(refusalSentence(args.status, args.serverMessage ?? null));
|
|
699
|
+
this.name = "WriteRefusedError";
|
|
700
|
+
this.status = args.status;
|
|
701
|
+
this.serverMessage = args.serverMessage ?? null;
|
|
702
|
+
this.technical = args.technical;
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
function serverMessageFromBody(body) {
|
|
706
|
+
if (!body || typeof body !== "object") return null;
|
|
707
|
+
const b = body;
|
|
708
|
+
const detail = b.detail;
|
|
709
|
+
const pick = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : null;
|
|
710
|
+
if (detail && typeof detail === "object" && !Array.isArray(detail)) {
|
|
711
|
+
const d = detail;
|
|
712
|
+
return pick(d.user_message) ?? pick(d.message);
|
|
713
|
+
}
|
|
714
|
+
return pick(b.user_message) ?? pick(detail) ?? pick(b.message);
|
|
715
|
+
}
|
|
716
|
+
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;
|
|
717
|
+
function personSentence(text2) {
|
|
718
|
+
if (typeof text2 !== "string") return null;
|
|
719
|
+
let said = text2.trim();
|
|
720
|
+
const named = /^[a-z][a-z0-9_]*[a-z0-9]: (.+)$/s.exec(said);
|
|
721
|
+
if (named) said = (named[1] ?? "").trim().replace(/^[a-z]/, (c) => c.toUpperCase());
|
|
722
|
+
if (said.length < 8 || LOOKS_TECHNICAL.test(said) || !/^[A-Z"“']/.test(said) || !/\s/.test(said)) return null;
|
|
723
|
+
return /[.!?]["”']?$/.test(said) ? said : `${said}.`;
|
|
724
|
+
}
|
|
725
|
+
function isPostgrestShaped(err) {
|
|
726
|
+
if (!err || typeof err !== "object") return false;
|
|
727
|
+
const code = err.code;
|
|
728
|
+
return typeof code === "string" && /^(PGRST\d{3}|[0-9A-Z]{5})$/.test(code) && !("status" in err && typeof err.status === "number");
|
|
729
|
+
}
|
|
730
|
+
function isSerializedRefusal(err) {
|
|
731
|
+
if (!err || typeof err !== "object" || err instanceof Error) return false;
|
|
732
|
+
const e = err;
|
|
733
|
+
if (typeof e.message !== "string" || e.message.trim() === "") return false;
|
|
734
|
+
if (e.name === "WriteRefusedError" || e.name === "WriteDidNotLandError") return true;
|
|
735
|
+
return e.name === "Error" && !LOOKS_TECHNICAL.test(e.message);
|
|
736
|
+
}
|
|
737
|
+
function postgrestSentence(code) {
|
|
738
|
+
if (code === "42501" || code === "PGRST301" || code === "PGRST302") return "You do not have permission to do this.";
|
|
739
|
+
if (code === "PGRST303" || code === "PGRST300") return "Your sign-in has expired. Sign in again, then try again.";
|
|
740
|
+
if (code === "PGRST116") return "It is no longer there \u2014 it may have been deleted.";
|
|
741
|
+
if (code === "23505") return "Something with the same name or key already exists.";
|
|
742
|
+
if (code === "23503") return "It refers to something that no longer exists.";
|
|
743
|
+
if (code === "23514" || code === "23502" || code === "22P02") return "The database would not accept that value.";
|
|
744
|
+
if (code === "57014") return "The database took too long to answer.";
|
|
745
|
+
if (code === "40001" || code === "40P01") return "It changed while you were working. Reload to see the latest.";
|
|
746
|
+
return "The database refused it.";
|
|
747
|
+
}
|
|
748
|
+
function assertWriteLanded(rows, technical) {
|
|
749
|
+
const n = Array.isArray(rows) ? rows.length : rows == null ? 0 : 1;
|
|
750
|
+
if (n > 0) return;
|
|
751
|
+
throw new WriteRefusedError({
|
|
752
|
+
status: 403,
|
|
753
|
+
serverMessage: "Nothing was saved: you may not have permission to change this, or it no longer exists.",
|
|
754
|
+
technical: `${technical} matched 0 rows`
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
function refusalSentence(status, serverMessage) {
|
|
758
|
+
const said = serverMessage && !LOOKS_TECHNICAL.test(serverMessage) ? serverMessage : null;
|
|
759
|
+
if (status === 401) return "Your sign-in has expired. Sign in again, then try again.";
|
|
760
|
+
if (status === 403) return said ?? "You do not have permission to do this.";
|
|
761
|
+
if (status === 404) return said ?? "It is no longer there \u2014 it may have been deleted.";
|
|
762
|
+
if (status === 409 || status === 412) return said ?? "It changed while you were working. Reload to see the latest.";
|
|
763
|
+
if (status >= 500) return "The server refused the request.";
|
|
764
|
+
if (said) return `The server said: ${said.replace(/\.?$/, ".")}`;
|
|
765
|
+
return "The server did not accept it.";
|
|
766
|
+
}
|
|
767
|
+
function describeWriteFailure(err, { action, remedy = "Try again." }) {
|
|
768
|
+
const title = `Could not ${action}.`;
|
|
769
|
+
let why;
|
|
770
|
+
if (err instanceof WriteRefusedError) {
|
|
771
|
+
why = err.message;
|
|
772
|
+
} else if (isSerializedRefusal(err)) {
|
|
773
|
+
why = /[.!?]["”']?$/.test(err.message.trim()) ? err.message.trim() : `${err.message.trim()}.`;
|
|
774
|
+
} else if (isPostgrestShaped(err)) {
|
|
775
|
+
why = personSentence(err.message) ?? postgrestSentence(err.code);
|
|
776
|
+
} else if (err && typeof err === "object" && "status" in err && typeof err.status === "number") {
|
|
777
|
+
const e = err;
|
|
778
|
+
why = refusalSentence(e.status, typeof e.userMessage === "string" ? e.userMessage : null);
|
|
779
|
+
} else if (err instanceof TypeError || err instanceof Error && /Failed to fetch|NetworkError|Load failed/i.test(err.message)) {
|
|
780
|
+
why = "The server could not be reached. Check your connection.";
|
|
781
|
+
} else if (err instanceof Error && err.message && !LOOKS_TECHNICAL.test(err.message)) {
|
|
782
|
+
why = err.message.replace(/\.?$/, ".");
|
|
783
|
+
} else if (typeof err === "string" && err.trim() && !LOOKS_TECHNICAL.test(err)) {
|
|
784
|
+
why = err.trim().replace(/\.?$/, ".");
|
|
785
|
+
} else {
|
|
786
|
+
why = "Something went wrong on our side.";
|
|
787
|
+
}
|
|
788
|
+
return { title, description: `${why} ${remedy}`.trim() };
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// src/db/write-one.ts
|
|
792
|
+
var import_supabase_js = require("@supabase/supabase-js");
|
|
793
|
+
var PAST = {
|
|
794
|
+
save: "saved",
|
|
795
|
+
update: "updated",
|
|
796
|
+
rename: "renamed",
|
|
797
|
+
move: "moved",
|
|
798
|
+
delete: "deleted",
|
|
799
|
+
remove: "removed",
|
|
800
|
+
archive: "archived",
|
|
801
|
+
restore: "restored",
|
|
802
|
+
publish: "published",
|
|
803
|
+
unpublish: "unpublished",
|
|
804
|
+
change: "changed"
|
|
805
|
+
};
|
|
806
|
+
var GERUND = {
|
|
807
|
+
save: "saving",
|
|
808
|
+
update: "updating",
|
|
809
|
+
rename: "renaming",
|
|
810
|
+
move: "moving",
|
|
811
|
+
delete: "deleting",
|
|
812
|
+
remove: "removing",
|
|
813
|
+
archive: "archiving",
|
|
814
|
+
restore: "restoring",
|
|
815
|
+
publish: "publishing",
|
|
816
|
+
unpublish: "unpublishing",
|
|
817
|
+
change: "changing"
|
|
818
|
+
};
|
|
819
|
+
var WriteDidNotLandError = class extends WriteRefusedError {
|
|
820
|
+
action;
|
|
821
|
+
noun;
|
|
822
|
+
reason;
|
|
823
|
+
constructor({ action, noun }, reason = "refused") {
|
|
824
|
+
super({
|
|
825
|
+
status: reason === "taken" ? 409 : 403,
|
|
826
|
+
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.`,
|
|
827
|
+
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`
|
|
828
|
+
});
|
|
829
|
+
this.name = "WriteDidNotLandError";
|
|
830
|
+
this.action = action;
|
|
831
|
+
this.noun = noun;
|
|
832
|
+
this.reason = reason;
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
async function writeOne(write, options) {
|
|
836
|
+
const { data, error } = await write;
|
|
837
|
+
if (error) throw error;
|
|
838
|
+
if (Array.isArray(data) && data.length > 0) return data[0];
|
|
839
|
+
if (options.alreadyDone) {
|
|
840
|
+
const current = await options.alreadyDone.reread();
|
|
841
|
+
if (current.error) throw current.error;
|
|
842
|
+
const row = Array.isArray(current.data) ? current.data[0] : current.data;
|
|
843
|
+
if (row && options.alreadyDone.isDone(row)) return row;
|
|
844
|
+
}
|
|
845
|
+
throw new WriteDidNotLandError(options, options.compareAndSet ? "taken" : "refused");
|
|
846
|
+
}
|
|
847
|
+
async function tryWriteOne(write, options) {
|
|
848
|
+
try {
|
|
849
|
+
return { row: await writeOne(write, options), error: null };
|
|
850
|
+
} catch (caught) {
|
|
851
|
+
if (caught instanceof Error) return { row: null, error: caught };
|
|
852
|
+
if (caught && typeof caught === "object" && "code" in caught && typeof caught.code === "string" && "message" in caught && typeof caught.message === "string") {
|
|
853
|
+
return { row: null, error: new import_supabase_js.PostgrestError({
|
|
854
|
+
code: caught.code,
|
|
855
|
+
message: caught.message,
|
|
856
|
+
details: "details" in caught && typeof caught.details === "string" ? caught.details : "",
|
|
857
|
+
hint: "hint" in caught && typeof caught.hint === "string" ? caught.hint : ""
|
|
858
|
+
}) };
|
|
859
|
+
}
|
|
860
|
+
const message = caught && typeof caught === "object" && "message" in caught && typeof caught.message === "string" ? caught.message : String(caught);
|
|
861
|
+
const error = new Error(message);
|
|
862
|
+
error.cause = caught;
|
|
863
|
+
return { row: null, error };
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function writeFailureStatus(error) {
|
|
867
|
+
if (error instanceof WriteDidNotLandError) return error.status;
|
|
868
|
+
if (error && typeof error === "object" && "code" in error) {
|
|
869
|
+
if (error.code === "42501") return 403;
|
|
870
|
+
if (error.code === "PGRST301" || error.code === "PGRST302" || error.code === "PGRST303") return 401;
|
|
871
|
+
if (error.code === "PGRST116") return 404;
|
|
872
|
+
if (error.code === "23505" || error.code === "40001" || error.code === "40P01") return 409;
|
|
873
|
+
}
|
|
874
|
+
return 500;
|
|
875
|
+
}
|
|
876
|
+
var BulkWriteError = class extends Error {
|
|
877
|
+
failures;
|
|
878
|
+
attempted;
|
|
879
|
+
constructor(failures, attempted) {
|
|
880
|
+
super(
|
|
881
|
+
`${failures.length} of ${attempted} ${attempted === 1 ? "change was" : "changes were"} not saved. ${failures[0]?.message ?? ""}`.trim()
|
|
882
|
+
);
|
|
883
|
+
this.name = "BulkWriteError";
|
|
884
|
+
this.failures = failures;
|
|
885
|
+
this.attempted = attempted;
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
async function writeOneRow(write, options) {
|
|
889
|
+
const { data, error } = await write;
|
|
890
|
+
if (error) {
|
|
891
|
+
if (error instanceof import_supabase_js.PostgrestError) return { data: null, error };
|
|
892
|
+
const e = error;
|
|
893
|
+
return {
|
|
894
|
+
data: null,
|
|
895
|
+
error: new import_supabase_js.PostgrestError({
|
|
896
|
+
message: typeof e.message === "string" ? e.message : String(error),
|
|
897
|
+
details: typeof e.details === "string" ? e.details : "",
|
|
898
|
+
hint: typeof e.hint === "string" ? e.hint : "",
|
|
899
|
+
code: typeof e.code === "string" ? e.code : ""
|
|
900
|
+
})
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
const rows = Array.isArray(data) ? data : [];
|
|
904
|
+
if (rows.length === 1) return { data: rows[0], error: null };
|
|
905
|
+
const refusal = new WriteDidNotLandError(options, options.compareAndSet ? "taken" : "refused");
|
|
906
|
+
return {
|
|
907
|
+
data: null,
|
|
908
|
+
error: new import_supabase_js.PostgrestError({
|
|
909
|
+
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.`,
|
|
910
|
+
details: rows.length === 0 ? refusal.technical : `writeOneRow(${options.action} ${options.noun}) matched ${rows.length} rows`,
|
|
911
|
+
hint: "",
|
|
912
|
+
code: "PGRST116"
|
|
913
|
+
})
|
|
914
|
+
};
|
|
915
|
+
}
|
|
678
916
|
//# sourceMappingURL=db.cjs.map
|