target-supabase-sdk 0.1.0 → 0.1.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.
@@ -1,74 +1,74 @@
1
- import { PostgrestError } from "@supabase/supabase-js";
2
-
3
- export interface Target {
4
- id: string;
5
- category: string;
6
- name: string;
7
- value: string;
8
- tagList: string[];
9
- extra?: string;
10
- details?: unknown;
11
- created_at: string;
12
- }
13
-
14
- export enum StatusCode {
15
- SUCCESS = 200,
16
- ERROR = 400,
17
- }
18
-
19
- export interface SupabaseResponse<T = unknown> {
20
- status_code: StatusCode;
21
- success: boolean;
22
- message?: string;
23
- data?: T;
24
- error?: {
25
- message: string;
26
- code?: string;
27
- };
28
- }
29
-
30
- export const createResponse = {
31
- success<T>(data?: T, message?: string): SupabaseResponse<T> {
32
- return {
33
- status_code: StatusCode.SUCCESS,
34
- success: true,
35
- message,
36
- data,
37
- };
38
- },
39
-
40
- error(
41
- message: string | { key: string; error: PostgrestError } | { key: string; error: string },
42
- statusCode?: StatusCode,
43
- code?: string
44
- ): SupabaseResponse {
45
- if (typeof message === "string") {
46
- return {
47
- status_code: statusCode ?? StatusCode.ERROR,
48
- success: false,
49
- error: {
50
- message,
51
- code,
52
- },
53
- };
54
- }
55
- if (typeof message?.error === "string") {
56
- return {
57
- status_code: statusCode ?? StatusCode.ERROR,
58
- success: false,
59
- error: {
60
- message: `Error from ${message.key}: ${message.error}`,
61
- code,
62
- },
63
- };
64
- }
65
- return {
66
- status_code: statusCode ?? StatusCode.ERROR,
67
- success: false,
68
- error: {
69
- message: `Error from ${message.key}: ${message?.error?.message}`,
70
- code,
71
- },
72
- };
73
- },
74
- };
1
+ import { PostgrestError } from "@supabase/supabase-js";
2
+
3
+ export interface Target {
4
+ id: string;
5
+ category: string;
6
+ name: string;
7
+ value: string;
8
+ tagList: string[];
9
+ extra?: string;
10
+ details?: unknown;
11
+ created_at: string;
12
+ }
13
+
14
+ export enum StatusCode {
15
+ SUCCESS = 200,
16
+ ERROR = 400,
17
+ }
18
+
19
+ export interface SupabaseResponse<T = unknown> {
20
+ status_code: StatusCode;
21
+ success: boolean;
22
+ message?: string;
23
+ data?: T;
24
+ error?: {
25
+ message: string;
26
+ code?: string;
27
+ };
28
+ }
29
+
30
+ export const createResponse = {
31
+ success<T>(data?: T, message?: string): SupabaseResponse<T> {
32
+ return {
33
+ status_code: StatusCode.SUCCESS,
34
+ success: true,
35
+ message,
36
+ data,
37
+ };
38
+ },
39
+
40
+ error(
41
+ message: string | { key: string; error: PostgrestError } | { key: string; error: string },
42
+ statusCode?: StatusCode,
43
+ code?: string
44
+ ): SupabaseResponse {
45
+ if (typeof message === "string") {
46
+ return {
47
+ status_code: statusCode ?? StatusCode.ERROR,
48
+ success: false,
49
+ error: {
50
+ message,
51
+ code,
52
+ },
53
+ };
54
+ }
55
+ if (typeof message?.error === "string") {
56
+ return {
57
+ status_code: statusCode ?? StatusCode.ERROR,
58
+ success: false,
59
+ error: {
60
+ message: `Error from ${message.key}: ${message.error}`,
61
+ code,
62
+ },
63
+ };
64
+ }
65
+ return {
66
+ status_code: statusCode ?? StatusCode.ERROR,
67
+ success: false,
68
+ error: {
69
+ message: `Error from ${message.key}: ${message?.error?.message}`,
70
+ code,
71
+ },
72
+ };
73
+ },
74
+ };
package/src/core.utils.ts CHANGED
@@ -1,98 +1,98 @@
1
- import { pick, pickBy } from "lodash-es";
2
-
3
- /** True when running in a development build (Vite or Node). Defaults to false for safety. */
4
- export function isDevEnvironment(): boolean {
5
- if (typeof import.meta !== "undefined" && "env" in import.meta) {
6
- const env = (import.meta as ImportMeta & { env?: { DEV?: boolean; MODE?: string } }).env;
7
- if (env?.DEV === true || env?.MODE === "development") return true;
8
- if (env?.DEV === false || env?.MODE === "production") return false;
9
- }
10
- if (typeof process !== "undefined" && process.env?.NODE_ENV) {
11
- return process.env.NODE_ENV !== "production";
12
- }
13
- return false;
14
- }
15
-
16
- export function logDev(...args: unknown[]): void {
17
- if (isDevEnvironment()) {
18
- console.log(...args);
19
- }
20
- }
21
-
22
- export function logDevGroup(label: string, fn: () => void): void {
23
- if (!isDevEnvironment()) return;
24
- console.group(label);
25
- try {
26
- fn();
27
- } finally {
28
- console.groupEnd();
29
- }
30
- }
31
-
32
- /** Log internal details, then throw a sanitized error safe to expose to callers. */
33
- export function handleSupabaseError(context: string, error: unknown, publicMessage?: string): never {
34
- console.error(`[${context}] failed:`, error);
35
- throw new Error(publicMessage ?? `[${context}] Operation failed`);
36
- }
37
-
38
- /** Raw payload -> Available data */
39
- export abstract class BaseValidator<T extends object> {
40
- protected requiredFields: (keyof T)[] = [];
41
- protected optionalFields: (keyof T)[] = [];
42
- protected ignoredFields: (keyof T)[] = [];
43
- protected customValidators: ((val: T) => true | string)[] = [];
44
-
45
- /** @deprecated -> addCustomValidator */
46
- addValidator(validator: (val: T) => true | string) {
47
- this.customValidators.push(validator);
48
- return this;
49
- }
50
-
51
- addCustomValidator(validator: (val: T) => true | string) {
52
- this.customValidators.push(validator);
53
- return this;
54
- }
55
-
56
- protected validateField(field: keyof T, value: unknown): boolean {
57
- // TODO now only empty check, later should add type checks
58
- return value != null;
59
- }
60
-
61
- validate = (val: T): T => {
62
- const errors: string[] = [];
63
-
64
- // Step 1: Check required fields
65
- this.requiredFields.forEach((field) => {
66
- if (!this.validateField(field, val[field])) {
67
- errors.push(`Missing required field: ${String(field)}`);
68
- }
69
- });
70
-
71
- // Step 2: Run custom validators
72
- this.customValidators.forEach((validator) => {
73
- const result = validator(val);
74
- if (result !== true) {
75
- errors.push(`Custom validation failed ${result}`);
76
- }
77
- });
78
-
79
- if (errors.length > 0) {
80
- const errorCount = errors.length;
81
- const errorList = errors.map((error, index) => ` ${index + 1}. ${error}`).join("\n");
82
-
83
- throw new Error(
84
- `[${this.constructor.name}] Validation failed (${errorCount} error${errorCount > 1 ? "s" : ""}):\n${errorList}`
85
- );
86
- }
87
-
88
- // Step 3: Pick required and optional fields
89
- const required = pick(val, this.requiredFields);
90
- const optional = pickBy(val, (value, key) => {
91
- const typedKey = key as keyof T;
92
- return this.optionalFields.includes(typedKey) && value != null;
93
- }) as Partial<T>;
94
- // TODO ignoredFields
95
-
96
- return { ...required, ...optional } as T;
97
- };
98
- }
1
+ import { pick, pickBy } from "lodash-es";
2
+
3
+ /** True when running in a development build (Vite or Node). Defaults to false for safety. */
4
+ export function isDevEnvironment(): boolean {
5
+ if (typeof import.meta !== "undefined" && "env" in import.meta) {
6
+ const env = (import.meta as ImportMeta & { env?: { DEV?: boolean; MODE?: string } }).env;
7
+ if (env?.DEV === true || env?.MODE === "development") return true;
8
+ if (env?.DEV === false || env?.MODE === "production") return false;
9
+ }
10
+ if (typeof process !== "undefined" && process.env?.NODE_ENV) {
11
+ return process.env.NODE_ENV !== "production";
12
+ }
13
+ return false;
14
+ }
15
+
16
+ export function logDev(...args: unknown[]): void {
17
+ if (isDevEnvironment()) {
18
+ console.log(...args);
19
+ }
20
+ }
21
+
22
+ export function logDevGroup(label: string, fn: () => void): void {
23
+ if (!isDevEnvironment()) return;
24
+ console.group(label);
25
+ try {
26
+ fn();
27
+ } finally {
28
+ console.groupEnd();
29
+ }
30
+ }
31
+
32
+ /** Log internal details, then throw a sanitized error safe to expose to callers. */
33
+ export function handleSupabaseError(context: string, error: unknown, publicMessage?: string): never {
34
+ console.error(`[${context}] failed:`, error);
35
+ throw new Error(publicMessage ?? `[${context}] Operation failed`);
36
+ }
37
+
38
+ /** Raw payload -> Available data */
39
+ export abstract class BaseValidator<T extends object> {
40
+ protected requiredFields: (keyof T)[] = [];
41
+ protected optionalFields: (keyof T)[] = [];
42
+ protected ignoredFields: (keyof T)[] = [];
43
+ protected customValidators: ((val: T) => true | string)[] = [];
44
+
45
+ /** @deprecated -> addCustomValidator */
46
+ addValidator(validator: (val: T) => true | string) {
47
+ this.customValidators.push(validator);
48
+ return this;
49
+ }
50
+
51
+ addCustomValidator(validator: (val: T) => true | string) {
52
+ this.customValidators.push(validator);
53
+ return this;
54
+ }
55
+
56
+ protected validateField(field: keyof T, value: unknown): boolean {
57
+ // TODO now only empty check, later should add type checks
58
+ return value != null;
59
+ }
60
+
61
+ validate = (val: T): T => {
62
+ const errors: string[] = [];
63
+
64
+ // Step 1: Check required fields
65
+ this.requiredFields.forEach((field) => {
66
+ if (!this.validateField(field, val[field])) {
67
+ errors.push(`Missing required field: ${String(field)}`);
68
+ }
69
+ });
70
+
71
+ // Step 2: Run custom validators
72
+ this.customValidators.forEach((validator) => {
73
+ const result = validator(val);
74
+ if (result !== true) {
75
+ errors.push(`Custom validation failed ${result}`);
76
+ }
77
+ });
78
+
79
+ if (errors.length > 0) {
80
+ const errorCount = errors.length;
81
+ const errorList = errors.map((error, index) => ` ${index + 1}. ${error}`).join("\n");
82
+
83
+ throw new Error(
84
+ `[${this.constructor.name}] Validation failed (${errorCount} error${errorCount > 1 ? "s" : ""}):\n${errorList}`
85
+ );
86
+ }
87
+
88
+ // Step 3: Pick required and optional fields
89
+ const required = pick(val, this.requiredFields);
90
+ const optional = pickBy(val, (value, key) => {
91
+ const typedKey = key as keyof T;
92
+ return this.optionalFields.includes(typedKey) && value != null;
93
+ }) as Partial<T>;
94
+ // TODO ignoredFields
95
+
96
+ return { ...required, ...optional } as T;
97
+ };
98
+ }
@@ -1,33 +1,33 @@
1
- import { Target } from "../core.interface";
2
-
3
- export interface Feed extends Target {
4
- category: CategoryFeed;
5
- details: FeedDetails;
6
- }
7
-
8
- export enum CategoryFeed {
9
- FEED = "feed",
10
- DOC = "doc",
11
- }
12
-
13
- export interface FeedDetails {
14
- manifestVersion: number;
15
- list: FeedItem[];
16
- }
17
-
18
- export enum FeedItemType {
19
- /** Plain Target */
20
- TEXT = "TEXT",
21
- /** Parcel */
22
- IMAGE = "IMAGE",
23
- /** Parcel */
24
- VIDEO = "VIDEO",
25
- /** TODO Review */
26
- // REVIEW = "REVIEW",
27
- }
28
-
29
- export interface FeedItem {
30
- index: number;
31
- type: FeedItemType;
32
- refTargetId: string;
33
- }
1
+ import { Target } from "../core.interface";
2
+
3
+ export interface Feed extends Target {
4
+ category: CategoryFeed;
5
+ details: FeedDetails;
6
+ }
7
+
8
+ export enum CategoryFeed {
9
+ FEED = "feed",
10
+ DOC = "doc",
11
+ }
12
+
13
+ export interface FeedDetails {
14
+ manifestVersion: number;
15
+ list: FeedItem[];
16
+ }
17
+
18
+ export enum FeedItemType {
19
+ /** Plain Target */
20
+ TEXT = "TEXT",
21
+ /** Parcel */
22
+ IMAGE = "IMAGE",
23
+ /** Parcel */
24
+ VIDEO = "VIDEO",
25
+ /** TODO Review */
26
+ // REVIEW = "REVIEW",
27
+ }
28
+
29
+ export interface FeedItem {
30
+ index: number;
31
+ type: FeedItemType;
32
+ refTargetId: string;
33
+ }
@@ -1,43 +1,43 @@
1
- import { createTarget } from "../core.api";
2
- import { BaseValidator } from "../core.utils";
3
- import { File, CategoryFile, FileDetails } from "./file.interface";
4
-
5
- export interface PostFileCreatePayload {
6
- name: File["name"];
7
- value: File["value"];
8
- category: File["category"];
9
- details: FileDetails;
10
- }
11
-
12
- export class PostFileValidator extends BaseValidator<PostFileCreatePayload> {
13
- protected requiredFields: (keyof PostFileCreatePayload)[] = ["name", "value", "category", "details"];
14
- protected optionalFields: (keyof PostFileCreatePayload)[] = [];
15
-
16
- constructor() {
17
- super();
18
- // Add custom
19
- this.addCustomValidator((val) => {
20
- return true;
21
- });
22
- }
23
- }
24
-
25
-
26
-
27
- export const postFileCreate = async (payload: PostFileCreatePayload) => {
28
- return createTarget<File, PostFileCreatePayload>({
29
- payload: payload,
30
- validator: PostFileValidator,
31
- createFn: (validPayload) => {
32
- const { name, value, category, details } = validPayload;
33
-
34
- return {
35
- name,
36
- category,
37
- value,
38
- tagList: [],
39
- details,
40
- };
41
- },
42
- });
43
- };
1
+ import { createTarget } from "../core.api";
2
+ import { BaseValidator } from "../core.utils";
3
+ import { File, CategoryFile, FileDetails } from "./file.interface";
4
+
5
+ export interface PostFileCreatePayload {
6
+ name: File["name"];
7
+ value: File["value"];
8
+ category: File["category"];
9
+ details: FileDetails;
10
+ }
11
+
12
+ export class PostFileValidator extends BaseValidator<PostFileCreatePayload> {
13
+ protected requiredFields: (keyof PostFileCreatePayload)[] = ["name", "value", "category", "details"];
14
+ protected optionalFields: (keyof PostFileCreatePayload)[] = [];
15
+
16
+ constructor() {
17
+ super();
18
+ // Add custom
19
+ this.addCustomValidator((val) => {
20
+ return true;
21
+ });
22
+ }
23
+ }
24
+
25
+
26
+
27
+ export const postFileCreate = async (payload: PostFileCreatePayload) => {
28
+ return createTarget<File, PostFileCreatePayload>({
29
+ payload: payload,
30
+ validator: PostFileValidator,
31
+ createFn: (validPayload) => {
32
+ const { name, value, category, details } = validPayload;
33
+
34
+ return {
35
+ name,
36
+ category,
37
+ value,
38
+ tagList: [],
39
+ details,
40
+ };
41
+ },
42
+ });
43
+ };
@@ -1,30 +1,30 @@
1
- import { Target } from "../core.interface";
2
-
3
- export enum CategoryFile {
4
- VIDEO = "video",
5
- IMAGE = "image",
6
- TEXT = "text",
7
- }
8
-
9
- export interface File extends Target {
10
- details: FileDetails;
11
- category: CategoryFile;
12
- }
13
-
14
- export interface FileDetails {
15
- manifestVersion: number;
16
- /** "abc.1" 唯一键 */
17
- value: string;
18
- hash: string | null;
19
- preview: FilePreview | null;
20
- /** Quickly access the file */
21
- url: string | null;
22
- parcelId: string | null;
23
- /** TODO 消費與改動歷史 */
24
- // reviewList?: string[];
25
- };
26
-
27
- export interface FilePreview {
28
- manifestVersion: number;
29
- refTargetId: string;
1
+ import { Target } from "../core.interface";
2
+
3
+ export enum CategoryFile {
4
+ VIDEO = "video",
5
+ IMAGE = "image",
6
+ TEXT = "text",
7
+ }
8
+
9
+ export interface File extends Target {
10
+ details: FileDetails;
11
+ category: CategoryFile;
12
+ }
13
+
14
+ export interface FileDetails {
15
+ manifestVersion: number;
16
+ /** "abc.1" 唯一键 */
17
+ value: string;
18
+ hash: string | null;
19
+ preview: FilePreview | null;
20
+ /** Quickly access the file */
21
+ url: string | null;
22
+ parcelId: string | null;
23
+ /** TODO 消費與改動歷史 */
24
+ // reviewList?: string[];
25
+ };
26
+
27
+ export interface FilePreview {
28
+ manifestVersion: number;
29
+ refTargetId: string;
30
30
  };