routesync 1.0.25 → 1.0.26

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/cli.js CHANGED
@@ -8773,9 +8773,9 @@ var SDKGenerator = class {
8773
8773
  const responseType = route.response ? `Types.${route.response.type}${route.response.collection ? "[]" : ""}` : `unknown`;
8774
8774
  lines.push(`export type ${ContractName} = {`);
8775
8775
  lines.push(` request: {`);
8776
- lines.push(` params: ${paramsType}`);
8776
+ lines.push(` params${paramsType === "unknown" ? "?" : ""}: ${paramsType}`);
8777
8777
  lines.push(` query?: Record<string, unknown>`);
8778
- lines.push(` body: ${bodyType}`);
8778
+ lines.push(` body${bodyType === "unknown" ? "?" : ""}: ${bodyType}`);
8779
8779
  lines.push(` }`);
8780
8780
  lines.push(` response: ${responseType}`);
8781
8781
  lines.push(`}`);
@@ -8887,9 +8887,9 @@ var TypeGenerator = class {
8887
8887
  for (const col of model.columns) {
8888
8888
  if (model.hidden && model.hidden.includes(col.name)) continue;
8889
8889
  const tsType = this.mapSqlTypeToTs(col.type);
8890
- const isOptional = col.nullable ? "?" : "";
8890
+ const finalTsType = col.nullable ? `${tsType} | null` : tsType;
8891
8891
  const safeName = camelCase(col.name).match(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/) ? camelCase(col.name) : `"${camelCase(col.name)}"`;
8892
- lines.push(` ${safeName}${isOptional}: ${tsType}`);
8892
+ lines.push(` ${safeName}: ${finalTsType}`);
8893
8893
  }
8894
8894
  if (model.appends && model.appends.length > 0) {
8895
8895
  for (const append of model.appends) {
@@ -9007,7 +9007,7 @@ var SchemaGenerator = class {
9007
9007
  return "z.boolean()";
9008
9008
  }
9009
9009
  if (type.includes("json")) {
9010
- return "z.record(z.unknown())";
9010
+ return "z.record(z.string(), z.unknown())";
9011
9011
  }
9012
9012
  const enumMatch = type.match(/^enum\((.*)\)$/);
9013
9013
  if (enumMatch && enumMatch[1]) {
@@ -9025,7 +9025,7 @@ var SchemaGenerator = class {
9025
9025
  return "z.boolean()";
9026
9026
  }
9027
9027
  if (type.includes("array") || type.includes("json") || type.includes("collection") || type.includes("object")) {
9028
- return "z.record(z.unknown())";
9028
+ return "z.record(z.string(), z.unknown())";
9029
9029
  }
9030
9030
  if (type.includes("date") || type.includes("datetime") || type.includes("string")) {
9031
9031
  return "z.string()";
@@ -9052,13 +9052,18 @@ var HookGenerator = class _HookGenerator {
9052
9052
  const method = route.method.toUpperCase();
9053
9053
  const hookName = _HookGenerator.toHookName(group, route.actionName);
9054
9054
  const queryKey = `['${group}', '${route.actionName}']`;
9055
+ const contractName = `${toTypeName(group)}${toTypeName(route.actionName)}Contract`;
9056
+ const hasParams = route.path.includes(":") || route.path.includes("{");
9057
+ const hasQuery = route.schema?.query || route.method.toUpperCase() === "GET";
9058
+ const hasBody = route.schema?.body;
9059
+ const requiresOptions = hasParams || hasBody || hasQuery && route.method.toUpperCase() !== "GET";
9055
9060
  if (method === "GET") {
9056
9061
  lines.push(`/**`);
9057
9062
  lines.push(` * @deprecated Generated hooks will become optional in v2.`);
9058
9063
  lines.push(` * Prefer \`useApiQuery(api.${group}.${route.actionName}, ...args)\` for future compatibility.`);
9059
9064
  lines.push(` */`);
9060
- lines.push(`export function ${hookName}(...args: Parameters<typeof api.${group}.${route.actionName}>) {`);
9061
- lines.push(` return useApiQuery(api.${group}.${route.actionName}, ...args as any)`);
9065
+ lines.push(`export function ${hookName}(...args: [options${requiresOptions ? "" : "?"}: import('./api').${contractName}['request'], queryOptions?: import('routesync/react').ApiQueryOptions<import('./api').${contractName}['response'], import('./types').ApiError>]) {`);
9066
+ lines.push(` return useApiQuery<import('./api').${contractName}['response'], import('./api').${contractName}['request']['params'], import('./api').${contractName}['request']['body'], import('./types').ApiError>(api.${group}.${route.actionName}, ...args)`);
9062
9067
  lines.push(`}`);
9063
9068
  lines.push(``);
9064
9069
  } else {
@@ -9089,7 +9094,7 @@ var NextActionGenerator = class {
9089
9094
  lines.push(`// Auto-generated Next.js Server Actions. Do not edit manually.`);
9090
9095
  lines.push(`"use server";`);
9091
9096
  lines.push(``);
9092
- lines.push(`import { api } from './api'`);
9097
+ lines.push(`IMPORT_PLACEHOLDER`);
9093
9098
  lines.push(`import { cookies } from 'next/headers'`);
9094
9099
  lines.push(``);
9095
9100
  lines.push(`// Helper to auto-inject token from cookies if available`);
@@ -9100,58 +9105,33 @@ var NextActionGenerator = class {
9100
9105
  lines.push(`}`);
9101
9106
  lines.push(``);
9102
9107
  const grouped = buildGeneratedRoutes(manifest.routes);
9108
+ const usedContracts = /* @__PURE__ */ new Set();
9103
9109
  for (const [groupName, routes] of Object.entries(grouped)) {
9104
9110
  for (const route of routes) {
9105
- const actionName = `${groupName}${toTypeName(route.actionName)}`;
9106
9111
  const TitleCaseGroup = groupName.charAt(0).toUpperCase() + groupName.slice(1);
9107
9112
  const TitleCaseAction = route.actionName.charAt(0).toUpperCase() + route.actionName.slice(1);
9108
9113
  const ContractName = `${TitleCaseGroup}${TitleCaseAction}Contract`;
9109
- const hasBody = route.schema && route.schema.rules && Object.keys(route.schema.rules).length > 0;
9110
- const hasQuery = route.method === "GET";
9111
- const hasParams = route.path.includes("{");
9112
- const count = (hasBody ? 1 : 0) + (hasQuery ? 1 : 0) + (hasParams ? 1 : 0);
9113
- let payloadParam = "";
9114
- const args = [];
9115
- if (count > 1) {
9116
- const props = [];
9117
- if (hasParams) {
9118
- props.push(`params: ${ContractName}['request']['params']`);
9119
- args.push(`params: payload.params`);
9120
- }
9121
- if (hasQuery) {
9122
- props.push(`query?: ${ContractName}['request']['query']`);
9123
- args.push(`query: payload.query`);
9124
- }
9125
- if (hasBody) {
9126
- props.push(`body: ${ContractName}['request']['body']`);
9127
- args.push(`body: payload.body`);
9128
- }
9129
- payloadParam = `payload: { ${props.join(", ")} }`;
9130
- } else if (count === 1) {
9131
- if (hasParams) {
9132
- payloadParam = `payload: ${ContractName}['request']['params']`;
9133
- args.push(`params: payload`);
9134
- }
9135
- if (hasQuery) {
9136
- payloadParam = `payload?: ${ContractName}['request']['query']`;
9137
- args.push(`query: payload`);
9138
- }
9139
- if (hasBody) {
9140
- payloadParam = `payload: ${ContractName}['request']['body']`;
9141
- args.push(`body: payload`);
9142
- }
9143
- } else {
9144
- payloadParam = `payload?: unknown`;
9145
- }
9146
- lines.push(`export async function ${actionName}Action(${payloadParam}) {`);
9147
- if (route.auth) {
9148
- args.push(`headers: await getAuthHeaders()`);
9149
- }
9150
- const argsString = args.length > 0 ? `{ ${args.join(", ")} }` : "";
9151
- const apiCall = `await api.${groupName}.${route.actionName}(${argsString})`;
9114
+ const actionFnName = `${groupName}${TitleCaseAction}Action`;
9115
+ const pathParams = Array.from(route.runtimePath.matchAll(/:([a-zA-Z0-9_]+)/g)).map((m) => m[1]);
9116
+ const hasParams = pathParams.length > 0;
9117
+ const hasBody = ["POST", "PUT", "PATCH", "DELETE"].includes(route.method) && route.schema?.rules && Object.keys(route.schema.rules).length > 0;
9118
+ const hasQuery = route.method === "GET" || route.method === "DELETE";
9119
+ usedContracts.add(ContractName);
9120
+ const sigParts = [];
9121
+ if (hasParams) sigParts.push(`params: ${ContractName}['request']['params']`);
9122
+ if (hasQuery) sigParts.push(`query?: ${ContractName}['request']['query']`);
9123
+ if (hasBody) sigParts.push(`body?: ${ContractName}['request']['body']`);
9124
+ const fnParam = sigParts.length > 0 ? `payload${hasParams ? "" : "?"}: { ${sigParts.join(", ")} }` : "";
9125
+ const callArgs = [];
9126
+ if (hasParams) callArgs.push(`params: payload.params`);
9127
+ if (hasQuery) callArgs.push(`query: payload?.query`);
9128
+ if (hasBody) callArgs.push(`body: payload?.body`);
9129
+ if (route.auth) callArgs.push(`headers: await getAuthHeaders()`);
9130
+ const argsStr = callArgs.length > 0 ? `{ ${callArgs.join(", ")} }` : "";
9131
+ lines.push(`export async function ${actionFnName}(${fnParam}) {`);
9152
9132
  lines.push(` try {`);
9153
- lines.push(` const response = ${apiCall}`);
9154
- lines.push(` return { success: true, data: response }`);
9133
+ lines.push(` const data = await api.${groupName}.${route.actionName}(${argsStr})`);
9134
+ lines.push(` return { success: true, data }`);
9155
9135
  lines.push(` } catch (error: unknown) {`);
9156
9136
  lines.push(` return { success: false, error: error instanceof Error ? error.message : String(error) }`);
9157
9137
  lines.push(` }`);
@@ -9159,11 +9139,10 @@ var NextActionGenerator = class {
9159
9139
  lines.push(``);
9160
9140
  }
9161
9141
  }
9162
- lines.splice(3, 1, `import { api, type ${Object.values(grouped).flatMap((routes) => routes.map((r) => {
9163
- const g = r.groupName || Object.keys(grouped).find((k) => grouped[k] === routes) || "";
9164
- return g.charAt(0).toUpperCase() + g.slice(1) + r.actionName.charAt(0).toUpperCase() + r.actionName.slice(1) + "Contract";
9165
- })).join(", type ")} } from './api'`);
9166
- await import_fs_extra7.default.writeFile(import_path6.default.join(outputDir, "actions.ts"), lines.join("\n"));
9142
+ const contractsToImport = Array.from(usedContracts);
9143
+ const importStr = contractsToImport.length > 0 ? `import { api, type ${contractsToImport.join(", type ")} } from './api'` : `import { api } from './api'`;
9144
+ const output = lines.join("\n").replace("IMPORT_PLACEHOLDER", importStr);
9145
+ await import_fs_extra7.default.writeFile(import_path6.default.join(outputDir, "actions.ts"), output);
9167
9146
  }
9168
9147
  };
9169
9148
 
package/dist/react.d.mts CHANGED
@@ -47,7 +47,9 @@ interface RouteDefinition<TResponse = unknown, TParams = unknown, TBody = unknow
47
47
 
48
48
  type EndpointCallableOptions<TParams, TBody> = (unknown extends TParams ? {} : {
49
49
  params: TParams;
50
- }) & (unknown extends TBody ? {} : {
50
+ }) & (unknown extends TBody ? {
51
+ body?: unknown;
52
+ } : {
51
53
  body: TBody;
52
54
  }) & {
53
55
  query?: Record<string, any>;
@@ -76,6 +78,7 @@ interface EndpointCallable<TResponse = unknown, TParams = unknown, TBody = unkno
76
78
  * const { data, isLoading } = useApiQuery(api.produk.list)
77
79
  * const { data } = useApiQuery(api.produk.detail, { params: { id: 10 } })
78
80
  */
81
+ type ApiQueryOptions<TResponse, TError = ApiError, TData = TResponse> = Omit<UseQueryOptions<TResponse, TError, TData>, 'queryKey' | 'queryFn'>;
79
82
  declare function useApiQuery<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError, TData = TResponse>(endpoint: EndpointCallable<TResponse, TParams, TBody>, ...args: [
80
83
  ...OptionalIfEmpty<EndpointCallableOptions<TParams, TBody>>,
81
84
  queryOptions?: Omit<UseQueryOptions<TResponse, TError, TData>, 'queryKey' | 'queryFn'>
@@ -171,4 +174,4 @@ interface UseFormSetError<TFieldValues extends Record<string, any>> {
171
174
  */
172
175
  declare function setFormErrors<TFieldValues extends Record<string, any>>(error: unknown, setError: UseFormSetError<TFieldValues>): void;
173
176
 
174
- export { type ApiMutationOptions, createHooks, setFormErrors, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiQueryClient, useApiSuspenseQuery };
177
+ export { type ApiMutationOptions, type ApiQueryOptions, createHooks, setFormErrors, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiQueryClient, useApiSuspenseQuery };
package/dist/react.d.ts CHANGED
@@ -47,7 +47,9 @@ interface RouteDefinition<TResponse = unknown, TParams = unknown, TBody = unknow
47
47
 
48
48
  type EndpointCallableOptions<TParams, TBody> = (unknown extends TParams ? {} : {
49
49
  params: TParams;
50
- }) & (unknown extends TBody ? {} : {
50
+ }) & (unknown extends TBody ? {
51
+ body?: unknown;
52
+ } : {
51
53
  body: TBody;
52
54
  }) & {
53
55
  query?: Record<string, any>;
@@ -76,6 +78,7 @@ interface EndpointCallable<TResponse = unknown, TParams = unknown, TBody = unkno
76
78
  * const { data, isLoading } = useApiQuery(api.produk.list)
77
79
  * const { data } = useApiQuery(api.produk.detail, { params: { id: 10 } })
78
80
  */
81
+ type ApiQueryOptions<TResponse, TError = ApiError, TData = TResponse> = Omit<UseQueryOptions<TResponse, TError, TData>, 'queryKey' | 'queryFn'>;
79
82
  declare function useApiQuery<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError, TData = TResponse>(endpoint: EndpointCallable<TResponse, TParams, TBody>, ...args: [
80
83
  ...OptionalIfEmpty<EndpointCallableOptions<TParams, TBody>>,
81
84
  queryOptions?: Omit<UseQueryOptions<TResponse, TError, TData>, 'queryKey' | 'queryFn'>
@@ -171,4 +174,4 @@ interface UseFormSetError<TFieldValues extends Record<string, any>> {
171
174
  */
172
175
  declare function setFormErrors<TFieldValues extends Record<string, any>>(error: unknown, setError: UseFormSetError<TFieldValues>): void;
173
176
 
174
- export { type ApiMutationOptions, createHooks, setFormErrors, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiQueryClient, useApiSuspenseQuery };
177
+ export { type ApiMutationOptions, type ApiQueryOptions, createHooks, setFormErrors, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiQueryClient, useApiSuspenseQuery };
package/dist/sdk.d.mts CHANGED
@@ -123,7 +123,9 @@ type CallOptions<TParams = unknown, TBody = unknown> = {
123
123
  };
124
124
  type EndpointCallableOptions<TParams, TBody> = (unknown extends TParams ? {} : {
125
125
  params: TParams;
126
- }) & (unknown extends TBody ? {} : {
126
+ }) & (unknown extends TBody ? {
127
+ body?: unknown;
128
+ } : {
127
129
  body: TBody;
128
130
  }) & {
129
131
  query?: Record<string, any>;
package/dist/sdk.d.ts CHANGED
@@ -123,7 +123,9 @@ type CallOptions<TParams = unknown, TBody = unknown> = {
123
123
  };
124
124
  type EndpointCallableOptions<TParams, TBody> = (unknown extends TParams ? {} : {
125
125
  params: TParams;
126
- }) & (unknown extends TBody ? {} : {
126
+ }) & (unknown extends TBody ? {
127
+ body?: unknown;
128
+ } : {
127
129
  body: TBody;
128
130
  }) & {
129
131
  query?: Record<string, any>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "routesync",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "Laravel routes to typed frontend SDKs.",
5
5
  "main": "./dist/sdk.js",
6
6
  "module": "./dist/sdk.mjs",